/* * Read until finding the boundary, saving to resultString or * resultFile. The boundary itself is not consumed. * * tossAtBoundary controls how many characters extra (<0) * or few (>0) are saved at the start of the boundary in the result. */ void CgiParser::readUntilBoundary(WebRequest& request, const std::string boundary, int tossAtBoundary, std::string *resultString, std::ostream *resultFile) { int bpos; while ((bpos = index(boundary)) == -1) { /* * If we couldn't find it. We need to wind the buffer, but only save * not including the boundary length. */ if (left_ == 0) throw WException("CgiParser: reached end of input while seeking end of " "headers or content. Format of CGI input is wrong"); /* save (up to) BUFSIZE from buffer to file or value string, but * mind the boundary length */ int save = std::min((buflen_ - (int)boundary.length()), (int)BUFSIZE); if (save > 0) { if (resultString) *resultString += std::string(buf_, save); if (resultFile) resultFile->write(buf_, save); /* wind buffer */ windBuffer(save); } unsigned amt = static_cast<unsigned> (std::min(left_, static_cast< ::int64_t >(BUFSIZE + MAXBOUND - buflen_))); request.in().read(buf_ + buflen_, amt); if (request.in().gcount() != (int)amt) throw WException("CgiParser: short read"); left_ -= amt; buflen_ += amt; } if (resultString) *resultString += std::string(buf_, bpos - tossAtBoundary); if (resultFile) resultFile->write(buf_, bpos - tossAtBoundary); /* wind buffer */ windBuffer(bpos); }
void CgiParser::parse(WebRequest& request, ReadOption readOption) { /* * TODO: optimize this ... */ request_ = &request; ::int64_t len = request.contentLength(); std::string type = request.contentType(); std::string meth = request.requestMethod(); request.postDataExceeded_ = (len > maxPostData_ ? len : 0); std::string queryString = request.queryString(); // XDomainRequest cannot set a contentType header, we therefore pass it // as a request parameter if (readOption != ReadHeadersOnly && meth == "POST" && (type.find("application/x-www-form-urlencoded") == 0 || queryString.find("&contentType=x-www-form-urlencoded") != std::string::npos)) { /* * TODO: parse this stream-based to avoid the malloc here. For now * we protect the maximum that can be POST'ed as form data. */ if (len > 5*1024*1024) throw WException("Oversized application/x-www-form-urlencoded (" + boost::lexical_cast<std::string>(len) + ")"); char *buf = new char[len + 1]; request.in().read(buf, len); if (request.in().gcount() != (int)len) { delete[] buf; throw WException("Unexpected short read."); } buf[len] = 0; // This is a special Wt feature, I do not think it standard. // For POST, parameters in url-encoded URL are still parsed. if (!queryString.empty()) queryString += '&'; queryString += buf; delete[] buf; } LOG_DEBUG("queryString (len=" << len << "): " << queryString); if (!queryString.empty()) Http::Request::parseFormUrlEncoded(queryString, request_->parameters_); if (readOption != ReadHeadersOnly && type.find("multipart/form-data") == 0) { if (meth != "POST") { throw WException("Invalid method for multipart/form-data: " + meth); } if (!request.postDataExceeded_) readMultipartData(request, type, len); else if (readOption == ReadBodyAnyway) { for (;len > 0;) { ::int64_t toRead = std::min(::int64_t(BUFSIZE), len); request.in().read(buf_, toRead); if (request.in().gcount() != (::int64_t)toRead) throw WException("CgiParser: short read"); len -= toRead; } } } }