void RequestParser::Test() {
  assert(ExtractMethod("GET / HTTP1.1") == GET);
  assert(ExtractMethod("HEAD / HTTP1.1") == HEAD);
  assert(ExtractMethod("POST / HTTP1.1") == POST);
  assert(ExtractMethod("Error / HTTP1.1") == UNKNOWN);

  assert(ExtractTarget("GET / HTTP1.1") == "");
  assert(ExtractTarget("GET // HTTP1.1") == "");
  assert(ExtractTarget("GET /// HTTP1.1") == "");
  assert(ExtractTarget("GET /dir/file.txt HTTP1.1") == "dir/file.txt");
  assert(ExtractTarget("GET /dir/file.txt?params HTTP1.1") == "dir/file.txt");
  assert(ExtractTarget("GET /?params HTTP1.1") == "");
  assert(ExtractTarget("GET //?params HTTP1.1") == "");

  assert(Flatten(ExtractQueryParams("GET / HTTP1.1")) == "");

  assert(Flatten(ExtractQueryParams("GET // HTTP1.1")) == "");
  assert(Flatten(ExtractQueryParams("GET /// HTTP1.1")) == "");
  assert(Flatten(ExtractQueryParams("GET /dir/file.txt HTTP1.1")) == "");
  assert(Flatten(ExtractQueryParams("GET /dir/file.txt? HTTP1.1")) == "");
  assert(Flatten(ExtractQueryParams("GET /dir/file.txt?params HTTP1.1")) == "params=''");
  assert(Flatten(ExtractQueryParams("GET /dir/file.txt?param1&param2&param3 HTTP1.1")) == "param1='' param2='' param3=''");
  assert(Flatten(ExtractQueryParams("GET /dir/file.txt?param1=val1&param2=val2&param3=val3 HTTP1.1")) == "param1='val1' param2='val2' param3='val3'");
  assert(Flatten(ExtractQueryParams("GET /?params HTTP1.1")) == "params=''");
  assert(Flatten(ExtractQueryParams("GET /? HTTP1.1")) == "");
  assert(Flatten(ExtractQueryParams("GET //?params HTTP1.1")) == "params=''");

  assert(TestRange(true, "Range: bytes=0-1024", 0, 1024));
  assert(TestRange(false, "Range: time=0-1024", 0, 0));
  assert(TestRange(true, "Range: bytes=0-", 0, -1));
  assert(TestRange(true, "Range: bytes=1024-", 1024, -1));
  assert(TestRange(true, "Range: bytes=232128512-", 232128512, -1));
}
void RequestParser::ParseRequestLine(const string& request) {
  // Extract method.
  method = ExtractMethod(request);
  target = ExtractTarget(request);
  params = ExtractQueryParams(request);
  //printf("Target: '%s'\n", target.c_str());
  //printf("Params: '%s'\n", Flatten(params).c_str());
}
  static void InternalCallback(struct mg_connection *connection,
                               const struct mg_request_info *request)
  {
    MongooseServer* that = reinterpret_cast<MongooseServer*>(request->user_data);

    MongooseOutputStream stream(connection);
    HttpOutput output(stream, that->IsKeepAliveEnabled());

    // Check remote calls
    if (!that->IsRemoteAccessAllowed() &&
        request->remote_ip != LOCALHOST)
    {
      output.SendUnauthorized(ORTHANC_REALM);
      return;
    }


    // Extract the HTTP headers
    IHttpHandler::Arguments headers;
    for (int i = 0; i < request->num_headers; i++)
    {
      std::string name = request->http_headers[i].name;
      std::transform(name.begin(), name.end(), name.begin(), ::tolower);
      headers.insert(std::make_pair(name, request->http_headers[i].value));
    }


    // Extract the GET arguments
    IHttpHandler::GetArguments argumentsGET;
    if (!strcmp(request->request_method, "GET"))
    {
      HttpToolbox::ParseGetArguments(argumentsGET, request->query_string);
    }


    // Compute the HTTP method, taking method faking into consideration
    HttpMethod method = HttpMethod_Get;
    if (!ExtractMethod(method, request, headers, argumentsGET))
    {
      output.SendStatus(HttpStatus_400_BadRequest);
      return;
    }


    // Authenticate this connection
    if (that->IsAuthenticationEnabled() && !IsAccessGranted(*that, headers))
    {
      output.SendUnauthorized(ORTHANC_REALM);
      return;
    }


    // Apply the filter, if it is installed
    const IIncomingHttpRequestFilter *filter = that->GetIncomingHttpRequestFilter();
    if (filter != NULL)
    {
      std::string username = GetAuthenticatedUsername(headers);

      char remoteIp[24];
      sprintf(remoteIp, "%d.%d.%d.%d", 
              reinterpret_cast<const uint8_t*>(&request->remote_ip) [3], 
              reinterpret_cast<const uint8_t*>(&request->remote_ip) [2], 
              reinterpret_cast<const uint8_t*>(&request->remote_ip) [1], 
              reinterpret_cast<const uint8_t*>(&request->remote_ip) [0]);

      if (!filter->IsAllowed(method, request->uri, remoteIp, username.c_str()))
      {
        output.SendUnauthorized(ORTHANC_REALM);
        return;
      }
    }


    // Extract the body of the request for PUT and POST

    // TODO Avoid unneccessary memcopy of the body

    std::string body;
    if (method == HttpMethod_Post ||
        method == HttpMethod_Put)
    {
      PostDataStatus status;

      IHttpHandler::Arguments::const_iterator ct = headers.find("content-type");
      if (ct == headers.end())
      {
        // No content-type specified. Assume no multi-part content occurs at this point.
        status = ReadBody(body, connection, headers);          
      }
      else
      {
        std::string contentType = ct->second;
        if (contentType.size() >= multipartLength &&
            !memcmp(contentType.c_str(), multipart, multipartLength))
        {
          status = ParseMultipartPost(body, connection, headers, contentType, that->GetChunkStore());
        }
        else
        {
          status = ReadBody(body, connection, headers);
        }
      }

      switch (status)
      {
        case PostDataStatus_NoLength:
          output.SendStatus(HttpStatus_411_LengthRequired);
          return;

        case PostDataStatus_Failure:
          output.SendStatus(HttpStatus_400_BadRequest);
          return;

        case PostDataStatus_Pending:
          output.SendBody();
          return;

        default:
          break;
      }
    }


    // Decompose the URI into its components
    UriComponents uri;
    try
    {
      Toolbox::SplitUriComponents(uri, request->uri);
    }
    catch (OrthancException)
    {
      output.SendStatus(HttpStatus_400_BadRequest);
      return;
    }


    LOG(INFO) << EnumerationToString(method) << " " << Toolbox::FlattenUri(uri);

    bool found = false;

    try
    {
      if (that->HasHandler())
      {
        found = that->GetHandler().Handle(output, method, uri, headers, argumentsGET, body.c_str(), body.size());
      }
    }
    catch (OrthancException& e)
    {
      // Using this candidate handler results in an exception
      LOG(ERROR) << "Exception in the HTTP handler: " << e.What();

      try
      {
        switch (e.GetErrorCode())
        {
          case ErrorCode_InexistentFile:
          case ErrorCode_InexistentItem:
          case ErrorCode_UnknownResource:
            output.SendStatus(HttpStatus_404_NotFound);
            break;

          case ErrorCode_BadRequest:
          case ErrorCode_UriSyntax:
            output.SendStatus(HttpStatus_400_BadRequest);
            break;

          default:
            output.SendStatus(HttpStatus_500_InternalServerError);
        }
      }
      catch (OrthancException&)
      {
        // An exception here reflects the fact that an exception was
        // triggered after the status code was sent by the HTTP handler.
      }

      return;
    }
    catch (boost::bad_lexical_cast&)
    {
      LOG(ERROR) << "Exception in the HTTP handler: Bad lexical cast";
      output.SendStatus(HttpStatus_400_BadRequest);
      return;
    }
    catch (std::runtime_error&)
    {
      LOG(ERROR) << "Exception in the HTTP handler: Presumably a bad JSON request";
      output.SendStatus(HttpStatus_400_BadRequest);
      return;
    }

    if (!found)
    {
      output.SendStatus(HttpStatus_404_NotFound);
    }
  }