libtasks Documentation  1.6
http_response.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2013-2014 ADTECH GmbH
3  * Licensed under MIT (https://github.com/adtechlabs/libtasks/blob/master/COPYING)
4  *
5  * Author: Andreas Pohl
6  */
7 
8 #include <iostream>
9 #include <cstring>
10 #include <cstdlib>
11 #include <sys/socket.h>
12 #include <boost/algorithm/string/predicate.hpp>
13 
14 #include <tasks/logging.h>
16 #include <tasks/net/socket.h>
17 
18 namespace tasks {
19 namespace net {
20 
22  // Status line
23  m_data_buffer.write("HTTP/1.1 ", 9);
24  m_data_buffer.write(m_status.c_str(), m_status.length());
26  // Headers
27  for (auto& kv : m_headers) {
28  m_data_buffer.write(kv.first.c_str(), kv.first.length());
29  m_data_buffer.write(": ", 2);
30  m_data_buffer.write(kv.second.c_str(), kv.second.length());
32  }
33  std::string ct = "Content-Length: " + std::to_string(m_content_buffer.size());
34  m_data_buffer.write(ct.c_str(), ct.length());
37 }
38 
39 // We are reading things into the content buffer only.
41  if (io_state::READY == m_state) {
44  }
45  if (io_state::DONE != m_state) {
46  std::streamsize towrite = 0, bytes = 0;
47  do {
48  towrite = m_content_buffer.to_write() - 1;
49  if (towrite < READ_BUFFER_SIZE_BLOCK - 1) {
51  towrite = m_content_buffer.to_write() - 1;
52  }
53  bytes = sock.read(m_content_buffer.ptr_write(), towrite);
54  if (bytes > 0) {
57  // Terminate the string for parsing
58  *(m_content_buffer.ptr_write()) = 0;
59  parse_data();
60  }
61  tdbg("http_response: read data successfully, " << bytes << " bytes" << std::endl);
63  *(m_content_buffer.ptr_write()) = 0;
67  }
68  }
69  } while (towrite == bytes);
70  }
71 }
72 
74  // find the next line break
75  char* eol = nullptr;
76  do {
77  if (*(m_content_buffer.ptr(m_last_line_start)) == '\n') {
79  }
80  eol = std::strstr(m_content_buffer.ptr(m_last_line_start), CRLF);
81  if (nullptr != eol) {
82  std::size_t len = eol - m_content_buffer.ptr(m_last_line_start);
83  if (len) {
84  *eol = 0;
85  parse_line();
86  m_last_line_start += len + 1;
87  } else {
88  // Second line break means content starts
89  if (m_chunked_enc) {
91  "http_response: Chunked transfer encoding needs to be implemented!");
92  } else if (!m_content_length_exists) {
94  "http_response: Invalid response: Content-Length header missing");
95  }
97  if (*(m_content_buffer.ptr(m_content_start)) == '\n') {
99  }
100  tdbg("http_response: Content starts at " << m_content_start << std::endl);
102  }
103  }
104  } while (nullptr != eol && 0 == m_content_start);
105 }
106 
108  if (0 == m_line_number) {
109  parse_status();
110  } else {
111  parse_header();
112  }
113  m_line_number++;
114 }
115 
117  // HTTP/#.# ### text
118  // Skip the first 5 bytes "HTTP/"
119  const char* space = std::strchr(m_content_buffer.ptr(m_last_line_start + 5), ' ');
120  m_status = space + 1;
121  m_status_code = std::atoi(space + 1);
122  tdbg("http_response: Status is " << m_status << std::endl);
123  if (m_status_code < 100 || m_status_code > 999) {
125  "http_response: Invalid status code " + std::to_string(m_status_code));
126  }
127 }
128 
130  char* eq = std::strchr(m_content_buffer.ptr(m_last_line_start), ':');
131  if (nullptr != eq) {
132  *eq = 0;
133  do {
134  eq++;
135  } while (*eq == ' ');
136  auto pair =
137  m_headers.insert(std::make_pair(std::string(m_content_buffer.ptr(m_last_line_start)), std::string(eq)));
138  if (pair.second) {
139  tdbg("http_response: Header: " << pair.first->first << " = " << pair.first->second << std::endl);
140  if (boost::iequals(pair.first->first, "Content-Length")) {
141  m_content_length = atoi(eq);
143  tdbg("http_response: Setting content length to " << m_content_length << std::endl);
144  } else if (boost::iequals(pair.first->first, "Transfer-Encoding")) {
145  if (pair.first->second == "chunked") {
146  m_chunked_enc = true;
147  }
148  }
149  }
150  } else {
152  "http_response: Invalid header: " + std::string(m_content_buffer.ptr(m_last_line_start)));
153  }
154 }
155 
156 } // net
157 } // tasks
tasks::tools::buffer m_data_buffer
Definition: http_base.h:130
std::size_t size() const
Definition: buffer.h:70
std::size_t offset_write() const
Definition: buffer.h:54
#define CRLF
Definition: http_base.h:24
std::size_t m_content_length
Definition: http_base.h:134
#define tdbg(m)
Definition: logging.h:54
The socket class.
Definition: socket.h:35
void move_ptr_write(std::size_t s)
Definition: buffer.h:58
char * ptr_write()
Definition: buffer.h:30
std::streamsize read(char *data, std::size_t len)
Definition: socket.cpp:251
std::size_t buffer_size()
Definition: buffer.h:89
Tasks execption class.
char * ptr(std::size_t pos)
Definition: buffer.h:36
std::unordered_map< std::string, std::string > m_headers
Definition: http_base.h:133
tasks::tools::buffer m_content_buffer
Definition: http_base.h:131
std::streamsize write(const char_type *data, std::streamsize size)
Definition: buffer.h:91
void read_data(net::socket &sock)
Read an HTTP response from a socket.
void prepare_data_buffer()
Prepare a HTTP request/response to be sent.
void set_size(std::size_t s)
Definition: buffer.h:72
#define CRLF_SIZE
Definition: http_base.h:25
Error on chunked encoding, that is not available yet.
std::streamsize to_write() const
Definition: buffer.h:66
void move_ptr_read_abs(std::size_t pos)
Definition: buffer.h:64
#define READ_BUFFER_SIZE_BLOCK
Definition: http_response.h:11