diff --git a/src/api/router/args.rs b/src/api/router/args.rs
index 0b693956..65dd8e9e 100644
--- a/src/api/router/args.rs
+++ b/src/api/router/args.rs
@@ -2,9 +2,10 @@ use std::{mem, ops::Deref};
use axum::{async_trait, body::Body, extract::FromRequest};
use bytes::{BufMut, Bytes, BytesMut};
-use conduit::{debug, err, utils::string::EMPTY, Error, Result};
+use conduit::{debug, debug_warn, err, trace, utils::string::EMPTY, Error, Result};
use ruma::{
- api::IncomingRequest, CanonicalJsonValue, DeviceId, OwnedDeviceId, OwnedServerName, OwnedUserId, ServerName, UserId,
+ api::IncomingRequest, CanonicalJsonObject, CanonicalJsonValue, DeviceId, OwnedDeviceId, OwnedServerName,
+ OwnedUserId, ServerName, UserId,
};
use service::Services;
@@ -85,6 +86,19 @@ where
async fn from_request(request: hyper::Request
, services: &State) -> Result {
let mut request = request::from(services, request).await?;
let mut json_body = serde_json::from_slice::(&request.body).ok();
+
+ // while very unusual and really shouldn't be recommended, Synapse accepts POST
+ // requests with a completely empty body. very old clients, libraries, and some
+ // appservices still call APIs like /join like this. so let's just default to
+ // empty object `{}` to copy synapse's behaviour
+ if json_body.is_none()
+ && request.parts.method == http::Method::POST
+ && !request.parts.uri.path().contains("/media/")
+ {
+ trace!("json_body from_request: {:?}", json_body.clone());
+ debug_warn!("received a POST request with an empty body, defaulting/assuming to {{}} like Synapse does");
+ json_body = Some(CanonicalJsonValue::Object(CanonicalJsonObject::new()));
+ }
let auth = auth::auth(services, &mut request, json_body.as_ref(), &T::METADATA).await?;
Ok(Self {
body: make_body::(services, &mut request, json_body.as_mut(), &auth)?,