Example #1
0
/*
 * 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);
}
Example #2
0
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;
      }
    }
  }
}