fastcgi++
A C++ FastCGI/Web API
request.cpp
Go to the documentation of this file.
1 
10 /*******************************************************************************
11 * Copyright (C) 2017 Eddie Carle [eddie@isatec.ca] *
12 * *
13 * This file is part of fastcgi++. *
14 * *
15 * fastcgi++ is free software: you can redistribute it and/or modify it under *
16 * the terms of the GNU Lesser General Public License as published by the Free *
17 * Software Foundation, either version 3 of the License, or (at your option) *
18 * any later version. *
19 * *
20 * fastcgi++ is distributed in the hope that it will be useful, but WITHOUT ANY *
21 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS *
22 * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for *
23 * more details. *
24 * *
25 * You should have received a copy of the GNU Lesser General Public License *
26 * along with fastcgi++. If not, see <http://www.gnu.org/licenses/>. *
27 *******************************************************************************/
28 
29 #include "fastcgi++/request.hpp"
30 #include "fastcgi++/log.hpp"
31 
32 template<class charT> void Fastcgipp::Request<charT>::complete()
33 {
34  out.flush();
35  err.flush();
36 
37  Block record(sizeof(Protocol::Header)+sizeof(Protocol::EndRequest));
38 
40  = *reinterpret_cast<Protocol::Header*>(record.begin());
41  header.version = Protocol::version;
42  header.type = Protocol::RecordType::END_REQUEST;
43  header.fcgiId = m_id.m_id;
44  header.contentLength = sizeof(Protocol::EndRequest);
45  header.paddingLength = 0;
46 
47  Protocol::EndRequest& body =
48  *reinterpret_cast<Protocol::EndRequest*>(record.begin()+sizeof(header));
49  body.appStatus = 0;
50  body.protocolStatus = m_status;
51 
52  m_send(m_id.m_socket, std::move(record), m_kill);
53 }
54 
55 template<class charT>
56 std::unique_lock<std::mutex>Fastcgipp::Request<charT>::handler()
57 {
58  std::unique_lock<std::mutex> lock(m_messagesMutex);
59  while(!m_messages.empty())
60  {
61  Message message = std::move(m_messages.front());
62  m_messages.pop();
63  lock.unlock();
64 
65  if(message.type == 0)
66  {
67  const Protocol::Header& header =
68  *reinterpret_cast<Protocol::Header*>(message.data.begin());
69  const auto body = message.data.begin()+sizeof(header);
70  const auto bodyEnd = body+header.contentLength;
71 
72  if(header.type == Protocol::RecordType::ABORT_REQUEST)
73  {
74  complete();
75  goto exit;
76  }
77 
78  if(header.type != m_state)
79  {
80  WARNING_LOG("Records received out of order from web server")
81  errorHandler();
82  goto exit;
83  }
84 
85  switch(m_state)
86  {
87  case Protocol::RecordType::PARAMS:
88  {
89  if(!(
90  role()==Protocol::Role::RESPONDER
91  || role()==Protocol::Role::AUTHORIZER))
92  {
93  m_status = Protocol::ProtocolStatus::UNKNOWN_ROLE;
94  WARNING_LOG("We got asked to do an unknown role")
95  errorHandler();
96  goto exit;
97  }
98 
99  if(header.contentLength == 0)
100  {
101  if(environment().contentLength > m_maxPostSize)
102  {
103  bigPostErrorHandler();
104  goto exit;
105  }
106  m_state = Protocol::RecordType::IN;
107  lock.lock();
108  continue;
109  }
110  m_environment.fill(body, bodyEnd);
111  lock.lock();
112  continue;
113  }
114 
115  case Protocol::RecordType::IN:
116  {
117  if(header.contentLength==0)
118  {
119  if(!inProcessor() && !m_environment.parsePostBuffer())
120  {
121  WARNING_LOG("Unknown content type from client")
122  errorHandler();
123  goto exit;
124  }
125 
126  m_environment.clearPostBuffer();
127  m_state = Protocol::RecordType::OUT;
128  break;
129  }
130 
131  if(m_environment.postBuffer().size()+(bodyEnd-body)
132  > environment().contentLength)
133  {
134  bigPostErrorHandler();
135  goto exit;
136  }
137 
138  m_environment.fillPostBuffer(body, bodyEnd);
139  inHandler(header.contentLength);
140  lock.lock();
141  continue;
142  }
143 
144  default:
145  {
146  ERROR_LOG("Our request is in a weird state.")
147  errorHandler();
148  goto exit;
149  }
150  }
151  }
152 
153  m_message = std::move(message);
154  if(response())
155  {
156  complete();
157  break;
158  }
159  lock.lock();
160  }
161 exit:
162  return lock;
163 }
164 
165 template<class charT> void Fastcgipp::Request<charT>::errorHandler()
166 {
167  out << \
168 "Status: 500 Internal Server Error\n"\
169 "Content-Type: text/html; charset=utf-8\r\n\r\n"\
170 "<!DOCTYPE html>"\
171 "<html lang='en'>"\
172  "<head>"\
173  "<title>500 Internal Server Error</title>"\
174  "</head>"\
175  "<body>"\
176  "<h1>500 Internal Server Error</h1>"\
177  "</body>"\
178 "</html>";
179 
180  complete();
181 }
182 
184 {
185  out << \
186 "Status: 413 Request Entity Too Large\n"\
187 "Content-Type: text/html; charset=utf-8\r\n\r\n"\
188 "<!DOCTYPE html>"\
189 "<html lang='en'>"\
190  "<head>"\
191  "<title>413 Request Entity Too Large</title>"\
192  "</head>"\
193  "<body>"\
194  "<h1>413 Request Entity Too Large</h1>"\
195  "</body>"\
196 "</html>";
197 
198  complete();
199 }
200 
201 template<class charT> void Fastcgipp::Request<charT>::configure(
202  const Protocol::RequestId& id,
203  const Protocol::Role& role,
204  bool kill,
205  const std::function<void(const Socket&, Block&&, bool)> send,
206  const std::function<void(Message)> callback)
207 {
208  using namespace std::placeholders;
209 
210  m_kill=kill;
211  m_id=id;
212  m_role=role;
213  m_callback=callback;
214  m_send=send;
215 
216  m_outStreamBuffer.configure(
217  id,
218  Protocol::RecordType::OUT,
219  std::bind(send, _1, _2, false));
220  m_errStreamBuffer.configure(
221  id,
222  Protocol::RecordType::ERR,
223  std::bind(send, _1, _2, false));
224 }
225 
226 template<class charT> unsigned Fastcgipp::Request<charT>::pickLocale(
227  const std::vector<std::string>& locales)
228 {
229  unsigned index=0;
230 
231  for(const std::string& language: environment().acceptLanguages)
232  {
233  if(language.size() <= 5)
234  {
235  const auto it = std::find_if(
236  locales.cbegin(),
237  locales.cend(),
238  [&language] (const std::string& locale)
239  {
240  return std::equal(
241  language.cbegin(),
242  language.cend(),
243  locale.cbegin());
244  });
245 
246  if(it != locales.cend())
247  {
248  index = it-locales.cbegin();
249  break;
250  }
251  }
252  }
253 
254  return index;
255 }
256 
257 template<class charT> void Fastcgipp::Request<charT>::setLocale(
258  const std::string& locale)
259 {
260  try
261  {
262  out.imbue(std::locale(locale+codepage()));
263  }
264  catch(...)
265  {
266  ERROR_LOG("Unable to set locale")
267  out.imbue(std::locale("C"));
268  }
269 }
270 
271 namespace Fastcgipp
272 {
273  template<> const char* Fastcgipp::Request<wchar_t>::codepage() const
274  {
275  return ".UTF-8";
276  }
277 
278  template<> const char* Fastcgipp::Request<char>::codepage() const
279  {
280  return "";
281  }
282 }
283 
284 template class Fastcgipp::Request<char>;
285 template class Fastcgipp::Request<wchar_t>;
BigEndian< FcgiId > fcgiId
Request ID.
Definition: protocol.hpp:301
BigEndian< uint16_t > contentLength
Content length.
Definition: protocol.hpp:304
The body for FastCGI records of type RecordType::END_REQUEST.
Definition: protocol.hpp:383
Topmost namespace for the fastcgi++ library.
const char version[]
Defines the fastcgi++ version.
Definition: protocol.cpp:91
std::unique_lock< std::mutex > handler()
Request Handler.
Definition: request.cpp:56
Request handling class
Definition: request.hpp:100
RecordType type
Record type.
Definition: protocol.hpp:298
void header(Level level)
Send a log header to logstream.
Definition: log.cpp:107
Data structure used to pass messages to requests.
Definition: message.hpp:46
Data structure used as the header for FastCGI records.
Definition: protocol.hpp:292
virtual void bigPostErrorHandler()
Called when too much post data is recieved.
Definition: request.cpp:183
void configure(const Protocol::RequestId &id, const Protocol::Role &role, bool kill, const std::function< void(const Socket &, Block &&, bool)> send, const std::function< void(Message)> callback)
Configures the request with the data it needs.
Definition: request.cpp:201
Class for representing an OS level I/O socket.
Definition: sockets.hpp:83
A unique identifier for each FastCGI request.
Definition: protocol.hpp:71
uint8_t version
FastCGI version number.
Definition: protocol.hpp:295
BigEndian< int32_t > appStatus
Return value.
Definition: protocol.hpp:386
unsigned pickLocale(const std::vector< std::string > &locales)
Pick a locale.
Definition: request.cpp:226
Block data
The raw data being passed along with the message.
Definition: message.hpp:75
void complete()
Generates an END_REQUEST FastCGI record.
Definition: request.cpp:32
Declares the Request class.
virtual void errorHandler()
Called when a processing error occurs.
Definition: request.cpp:165
void setLocale(const std::string &locale)
Set the output stream&#39;s locale.
Definition: request.cpp:257
#define ERROR_LOG(data)
Log any "errors" that can be recovered from.
Definition: log.hpp:107
Declares the Fastcgipp debugging/logging facilities.
const char * codepage() const
Codepage.
uint8_t paddingLength
Length of record padding.
Definition: protocol.hpp:307
Role
Defines the possible roles a FastCGI application may play.
Definition: protocol.hpp:163
ProtocolStatus protocolStatus
Requests Status.
Definition: protocol.hpp:389
Data structure to hold a block of raw data.
Definition: block.hpp:44
int type
Type of message. A 0 means FastCGI record. Anything else is open.
Definition: message.hpp:72
#define WARNING_LOG(data)
Log any externally caused "errors".
Definition: log.hpp:124
char * begin()
Pointer to the first element.
Definition: block.hpp:103