Skip to content

Commit af867c1

Browse files
authored
Allow large form posts via multipart encoded forms to command API (#5000)
This temporarily puts this under a feature flag, to make debugging a little easier, but initial tests show things working as expected. This allows for a large form post to be chunked up and POST, where we were noticing a number of different issues with body size before.
1 parent 59d9e3e commit af867c1

File tree

3 files changed

+24
-1
lines changed

3 files changed

+24
-1
lines changed

readthedocs/doc_builder/environments.py

+20-1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
from docker.errors import APIError as DockerAPIError
1818
from docker.errors import DockerException
1919
from requests.exceptions import ConnectionError
20+
from requests_toolbelt.multipart.encoder import MultipartEncoder
2021
from slumber.exceptions import HttpClientError
2122

2223
from readthedocs.builds.constants import BUILD_STATE_FINISHED
@@ -266,7 +267,25 @@ def save(self):
266267
'start_time': self.start_time,
267268
'end_time': self.end_time,
268269
}
269-
api_v2.command.post(data)
270+
271+
if self.build_env.project.has_feature(Feature.API_LARGE_DATA):
272+
# Don't use slumber directly here. Slumber tries to enforce a string,
273+
# which will break our multipart encoding here.
274+
encoder = MultipartEncoder(
275+
{key: str(value) for key, value in data.items()}
276+
)
277+
resource = api_v2.command
278+
resp = resource._store['session'].post(
279+
resource._store['base_url'] + '/',
280+
data=encoder,
281+
headers={
282+
'Content-Type': encoder.content_type,
283+
}
284+
)
285+
log.info('Post response via multipart form: %s', resp)
286+
else:
287+
resp = api_v2.command.post(data)
288+
log.info('Post response via JSON encoded data: %s', resp)
270289

271290

272291
class DockerBuildCommand(BuildCommand):

readthedocs/projects/models.py

+2
Original file line numberDiff line numberDiff line change
@@ -1239,6 +1239,7 @@ def add_features(sender, **kwargs):
12391239
DONT_OVERWRITE_SPHINX_CONTEXT = 'dont_overwrite_sphinx_context'
12401240
ALLOW_V2_CONFIG_FILE = 'allow_v2_config_file'
12411241
MKDOCS_THEME_RTD = 'mkdocs_theme_rtd'
1242+
API_LARGE_DATA = 'api_large_data'
12421243
DONT_SHALLOW_CLONE = 'dont_shallow_clone'
12431244
USE_TESTING_BUILD_IMAGE = 'use_testing_build_image'
12441245

@@ -1276,6 +1277,7 @@ def add_features(sender, **kwargs):
12761277
'Use Docker image labelled as `testing` to build the docs',
12771278
),
12781279
),
1280+
(API_LARGE_DATA, _('Try alternative method of posting large data'))
12791281
)
12801282

12811283
projects = models.ManyToManyField(

readthedocs/restapi/views/model_views.py

+2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
from django.shortcuts import get_object_or_404
99
from django.template.loader import render_to_string
1010
from rest_framework import decorators, permissions, status, viewsets
11+
from rest_framework.parsers import MultiPartParser, JSONParser, FormParser
1112
from rest_framework.renderers import BaseRenderer, JSONRenderer
1213
from rest_framework.response import Response
1314

@@ -277,6 +278,7 @@ class BuildViewSet(SettingsOverrideObject):
277278

278279

279280
class BuildCommandViewSet(UserSelectViewSet):
281+
parser_classes = [JSONParser, MultiPartParser]
280282
permission_classes = [APIRestrictedPermission]
281283
renderer_classes = (JSONRenderer,)
282284
serializer_class = BuildCommandSerializer

0 commit comments

Comments
 (0)