1

I have a CPPCMS (V1.1.0) server (sample code below) and a C# (.NET 4.0) client (sample code below) and I'm trying to POST a file from the C# client to the CPPCMS server.

The server works if I POST to it from PaleMoon or a CURL (V7.56.1) client (sample code below), so I don't think the problem is in the server code.

The client works if I POST from it to http://posttestserver.com, so I don't think the problem is in the client code.

However, when I try my "working" CPPCMS server against my "working" C# client, I get a 400 Bad Request response. The response is generated by CPPCMS (my first server log is never written as application::main is not called), though CPPCMS does not show or throw any error on the server, even with debug logging enabled.

So I have a client and a server that both work on their own, but not together. Looking at the network trace in Microsoft Network Monitor 3.4 and TCPDUMP, I cannot see what's functionally different between a working request and a failing one. The trace suggests that CPPCMS fails on the very first packet of the POST (of a multi-packet POST request), though this may be a timing issue in the trace, I don't know.

Notes

  • The CPPCMS server is running on Ubuntu 16.04
  • The C# client is running on Windows 7
  • The working CURL test was done from the server machine
  • The working PaleMoon test was done from the client machine
  • I copied the code from Ubuntu into this post by hand, so I may have errors in their samples below

CPPCMS web-server : main.cpp

#include <cppcms/service.h> #include <cppcms/applications_pool.h> #include <cppcms/application.h> #include <cppcms/http_request.h> #include <iostream> #include <string> class Application : public cppcms::application { public: Application ( cppcms::service & service ): cppcms::application(service) { } virtual void main ( std::string url ){ std::cout << "Request for " << url << std::endl << " Uploaded file count " << request().files().size() << std::endl; }; int main(int argc, char * * args) { cppcms::service service(argc, args); service.applications_pool().mount ( cppcms::applications_factory<Application>() ); service.run(); return 0; } // g++ -o test main.cpp -lcppcms -lbooster -std=c++11 // ./test -c config.js 

CPPCMS web-server : config.js

{ "service": { "api": "http" , "port": 8080 , "ip": "10.0.0.4" } , "http": { "script": "/" } , "logging": { "level": "debug" } } 

C# client : main.cs

using System; using System.Net.Http; namespace CPPCMSTest { class Program { static void Main(string[] args) { using(var client = new HttpClient()) using(var content = new MultipartFormDataContent()) { content.Add(new StringContent("test"), "name", "filename"); Console.WriteLine ( client . PostAsync("http://10.0.0.4:8080", content) . Result . Content . ReadAsStringAsync() . Result ); } } } } // Output is: // <html> // <body> // <h1>400 Bad Request</h1> // </body> // </html> // // CPPCMS output is: // 2018-01-19 12:34:56; cppcms_http, info: POST / (http_api.cpp:249) // // That is, CPPCMS doesn't show an error and doesn't reach the application::main 

CURL client : main.cpp

#include <curl/curl.h> #include <iostream> int main(int argc, char * * args) { curl_global_init(CURL_GLOBAL_DEFAULT); CURL * curl(curl_easy_init()); curl_mime * mime(curl_mime_init(curl)); curl_mime_filedata(curl_mime_addpart(mime), "main.cpp"); curl_easy_setopt(curl, CURLOPT_MIMEPOST, mime); curl_easy_setopt(curl, CURLOPT_URL, "http://10.0.0.4:8080"); curl_easy_perform(curl); curl_mime_free(mime); long responseCode; curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &responseCode); curl_easy_cleanup(curl); std::cout << "Response code is " << responseCode << std::endl; return 0; } // g++ -o test main.cpp -lcurl -std=c++11 // ./test // Response code is 200 // // CPPCMS output is: // 2018-01-19 12:34:56; cppcms_http, info: POST / (http_api.cpp:249) // Request for // Uploaded file count 1 

1 Answer 1

1

I've traced the problem back to a bug in CPPCMS. When it parses the headers of an attachment, if the value in the name/value pair is unquoted, it does not correctly position the parser after the value. This causes it to fail because it's expecting a semi-colon or end-of-line, and instead gets the beginning of the previous value. I've submitted a bug report to CPPCMS.

If the value is quoted, there is no problem, but I can't get C#'s HttpClient to quote all values for Content-Disposition -- it is possible to quote the "name" and "filename" parts (and I've seen another StackOverflow post that shows this as a source of issues), however, HttpClient also adds a "filename*" part, and there seems to be no way of quoting this (as HttpClient replaces literal quotes with %22).

If anyone else has this problem and needs to update the CPPCMS code themselves, the bug is in private/multipart_parser.h in the parse_pair function. At the end of that function there is an if(*p=='"') ... else ... statement. Under the true condition (the working quoted parser), the block ends with p=tmp;, but under the false condition (the buggy unquoted parser), the block ends with tmp=p;. Updating this to p=tmp; fixes the problem.

Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.