Multipart form data parser

Features

  • No dependencies

  • Works with chunks of a data - no need to buffer the whole request

  • Almost no internal buffering. Buffer size doesn’t exceed the size of the boundary (~60-70 bytes)

Tested as part of Cosmonaut HTTP server.

Implementation based on node-formidable by Felix Geisendörfer.

Inspired by http-parser by Ryan Dahl.

Usage (C)

This parser library works with several callbacks, which the user may set up at application initialization time.

multipart_parser_settings callbacks;

memset(&callbacks, 0, sizeof(multipart_parser_settings));

callbacks.on_header_field = read_header_name;
callbacks.on_header_value = read_header_value;

These functions must match the signatures defined in the multipart-parser header file. For this simple example, we’ll just use two of the available callbacks to print all headers the library finds in multipart messages.

Returning a value other than 0 from the callbacks will abort message processing.

int read_header_name(multipart_parser* p, const char *at, size_t length)
{
   printf("%.*s: ", length, at);
   return 0;
}

int read_header_value(multipart_parser* p, const char *at, size_t length)
{
   printf("%.*s\n", length, at);
   return 0;
}

When a message arrives, callers must parse the multipart boundary from the Content-Type header (see the RFC for more information and examples), and then execute the parser.

multipart_parser* parser = multipart_parser_init(boundary, &callbacks);
multipart_parser_execute(parser, body, length);
multipart_parser_free(parser);

Usage (C++)

In C++, when the callbacks are static member functions it may be helpful to pass the instantiated multipart consumer along as context. The following (abbreviated) class called MultipartConsumer shows how to pass this to callback functions in order to access non-static member data.

class MultipartConsumer
{
public:
    MultipartConsumer(const std::string& boundary)
    {
        memset(&m_callbacks, 0, sizeof(multipart_parser_settings));
        m_callbacks.on_header_field = ReadHeaderName;
        m_callbacks.on_header_value = ReadHeaderValue;

        m_parser = multipart_parser_init(boundary.c_str(), &m_callbacks);
        multipart_parser_set_data(m_parser, this);
    }

    ~MultipartConsumer()
    {
        multipart_parser_free(m_parser);
    }

    int CountHeaders(const std::string& body)
    {
        multipart_parser_execute(m_parser, body.c_str(), body.size());
        return m_headers;
    }

private:
    static int ReadHeaderName(multipart_parser* p, const char *at, size_t length)
    {
        MultipartConsumer* me = (MultipartConsumer*)multipart_parser_get_data(p);
        me->m_headers++;
    }

    multipart_parser* m_parser;
    multipart_parser_settings m_callbacks;
    int m_headers;
};

Contributors

© 2012 Igor Afonov