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);
    }
  }