Crow  0.3
A C++ microframework for the web
multipart.h
1 #pragma once
2 
3 #include <string>
4 #include <vector>
5 #include <sstream>
6 
7 #include "crow/http_request.h"
8 #include "crow/returnable.h"
9 
10 namespace crow
11 {
12  ///Encapsulates anything related to processing and organizing `multipart/xyz` messages
13  namespace multipart
14  {
15  const std::string dd = "--";
16  const std::string crlf = "\r\n";
17 
18  ///The first part in a section, contains metadata about the part
19  struct header
20  {
21  std::pair<std::string, std::string> value; ///< The first part of the header, usually `Content-Type` or `Content-Disposition`
22  std::unordered_map<std::string, std::string> params; ///< The parameters of the header, come after the `value`
23  };
24 
25  ///One part of the multipart message
26 
27  ///It is usually separated from other sections by a `boundary`
28  ///
29  struct part
30  {
31  std::vector<header> headers; ///< (optional) The first part before the data, Contains information regarding the type of data and encoding
32  std::string body; ///< The actual data in the part
33  };
34 
35  ///The parsed multipart request/response
36  struct message : public returnable
37  {
38  ci_map headers;
39  std::string boundary; ///< The text boundary that separates different `parts`
40  std::vector<part> parts; ///< The individual parts of the message
41 
42  const std::string& get_header_value(const std::string& key) const
43  {
44  return crow::get_header_value(headers, key);
45  }
46 
47  ///Represent all parts as a string (**does not include message headers**)
48  std::string dump() const override
49  {
50  std::stringstream str;
51  std::string delimiter = dd + boundary;
52 
53  for (unsigned i=0 ; i<parts.size(); i++)
54  {
55  str << delimiter << crlf;
56  str << dump(i);
57  }
58  str << delimiter << dd << crlf;
59  return str.str();
60  }
61 
62  ///Represent an individual part as a string
63  std::string dump(int part_) const
64  {
65  std::stringstream str;
66  part item = parts[part_];
67  for (header item_h: item.headers)
68  {
69  str << item_h.value.first << ": " << item_h.value.second;
70  for (auto& it: item_h.params)
71  {
72  str << "; " << it.first << '=' << pad(it.second);
73  }
74  str << crlf;
75  }
76  str << crlf;
77  str << item.body << crlf;
78  return str.str();
79  }
80 
81  ///Default constructor using default values
82  message(const ci_map& headers, const std::string& boundary, const std::vector<part>& sections)
83  : returnable("multipart/form-data"), headers(headers), boundary(boundary), parts(sections){}
84 
85  ///Create a multipart message from a request data
86  message(const request& req)
87  : returnable("multipart/form-data"),
88  headers(req.headers),
89  boundary(get_boundary(get_header_value("Content-Type"))),
90  parts(parse_body(req.body))
91  {}
92 
93  private:
94 
95  std::string get_boundary(const std::string& header) const
96  {
97  size_t found = header.find("boundary=");
98  if (found)
99  return header.substr(found+9);
100  return std::string();
101  }
102 
103  std::vector<part> parse_body(std::string body)
104  {
105 
106  std::vector<part> sections;
107 
108  std::string delimiter = dd + boundary;
109 
110  while(body != (crlf))
111  {
112  size_t found = body.find(delimiter);
113  std::string section = body.substr(0, found);
114 
115  //+2 is the CRLF
116  //We don't check it and delete it so that the same delimiter can be used for
117  //the last delimiter (--delimiter--CRLF).
118  body.erase(0, found + delimiter.length() + 2);
119  if (!section.empty())
120  {
121  sections.emplace_back(parse_section(section));
122  }
123  }
124  return sections;
125  }
126 
127  part parse_section(std::string& section)
128  {
129  struct part to_return;
130 
131  size_t found = section.find(crlf+crlf);
132  std::string head_line = section.substr(0, found+2);
133  section.erase(0, found + 4);
134 
135  parse_section_head(head_line, to_return);
136  to_return.body = section.substr(0, section.length()-2);
137  return to_return;
138  }
139 
140  void parse_section_head(std::string& lines, part& part)
141  {
142  while (!lines.empty())
143  {
144  header to_add;
145 
146  size_t found = lines.find(crlf);
147  std::string line = lines.substr(0, found);
148  lines.erase(0, found+2);
149  //add the header if available
150  if (!line.empty())
151  {
152  size_t found = line.find("; ");
153  std::string header = line.substr(0, found);
154  if (found != std::string::npos)
155  line.erase(0, found+2);
156  else
157  line = std::string();
158 
159  size_t header_split = header.find(": ");
160 
161  to_add.value = std::pair<std::string, std::string>(header.substr(0, header_split), header.substr(header_split+2));
162  }
163 
164  //add the parameters
165  while (!line.empty())
166  {
167  size_t found = line.find("; ");
168  std::string param = line.substr(0, found);
169  if (found != std::string::npos)
170  line.erase(0, found+2);
171  else
172  line = std::string();
173 
174  size_t param_split = param.find('=');
175 
176  std::string value = param.substr(param_split+1);
177 
178  to_add.params.emplace(param.substr(0, param_split), trim(value));
179  }
180  part.headers.emplace_back(to_add);
181  }
182  }
183 
184  inline std::string trim (std::string& string, const char& excess = '"') const
185  {
186  if (string.length() > 1 && string[0] == excess && string[string.length()-1] == excess)
187  return string.substr(1, string.length()-2);
188  return string;
189  }
190 
191  inline std::string pad (std::string& string, const char& padding = '"') const
192  {
193  return (padding + string + padding);
194  }
195 
196  };
197  }
198 }
crow::multipart::message::parts
std::vector< part > parts
The individual parts of the message.
Definition: multipart.h:40
crow::multipart::part::headers
std::vector< header > headers
(optional) The first part before the data, Contains information regarding the type of data and encodi...
Definition: multipart.h:31
crow::multipart::header::params
std::unordered_map< std::string, std::string > params
The parameters of the header, come after the value
Definition: multipart.h:22
crow::multipart::message::message
message(const ci_map &headers, const std::string &boundary, const std::vector< part > &sections)
Default constructor using default values.
Definition: multipart.h:82
crow::multipart::header
The first part in a section, contains metadata about the part.
Definition: multipart.h:19
crow::multipart::header::value
std::pair< std::string, std::string > value
The first part of the header, usually Content-Type or Content-Disposition
Definition: multipart.h:21
crow::multipart::message::message
message(const request &req)
Create a multipart message from a request data.
Definition: multipart.h:86
crow::request
An HTTP request.
Definition: http_request.h:26
crow::multipart::message::dump
std::string dump() const override
Represent all parts as a string (does not include message headers)
Definition: multipart.h:48
crow::multipart::message::boundary
std::string boundary
The text boundary that separates different parts
Definition: multipart.h:39
crow::multipart::part
One part of the multipart message.
Definition: multipart.h:29
crow::multipart::message::dump
std::string dump(int part_) const
Represent an individual part as a string.
Definition: multipart.h:63
crow::returnable
An abstract class that allows any other class to be returned by a handler.
Definition: returnable.h:8
crow::multipart::message
The parsed multipart request/response.
Definition: multipart.h:36
crow::multipart::part::body
std::string body
The actual data in the part.
Definition: multipart.h:32