Skip to content

Commit caa0816

Browse files
committed
A more complete fix for #588
1 parent f1c0feb commit caa0816

3 files changed

Lines changed: 63 additions & 23 deletions

File tree

ChangeLog

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,13 @@
1+
2026-03-12 Richard Frith-Macdonald <rfm@gnu.org>
2+
3+
* Source/GSHTTPURLHandle.m:
4+
* Source/NSURLProtocol.m:
5+
Adjust to only add a Content-Length header for a zero length body when
6+
the method of the request requires it (POST, PUT, PATCH). In other
7+
cases we should only add that header if the request has a body.
8+
Issue pointed out by William Everett referencing RFC-9110 and saying
9+
at least some web servers object to Content-length in GET requests.
10+
111
2026-03-10 Richard Frith-Macdonald <rfm@gnu.org>
212

313
* Source/GSSocketStream.m:

Source/GSHTTPURLHandle.m

Lines changed: 39 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -447,7 +447,9 @@ - (void) bgdApply: (NSString*)basic
447447
NSMutableData *buf;
448448
NSMutableData *masked = nil;
449449
NSString *version;
450+
NSString *method;
450451
NSMapEnumerator enumerator;
452+
NSUInteger cl;
451453

452454
RETAIN(self);
453455
if (debug)
@@ -501,15 +503,48 @@ - (void) bgdApply: (NSString*)basic
501503
}
502504
}
503505

504-
/* Ensure we set the correct content length (may be zero)
506+
cl = [wData length];
507+
508+
/* Use default method if none is set.
509+
*/
510+
method = [request objectForKey: GSHTTPPropertyMethodKey];
511+
if (method == nil)
512+
{
513+
if (cl > 0)
514+
{
515+
method = @"POST";
516+
}
517+
else
518+
{
519+
method = @"GET";
520+
}
521+
}
522+
else if ([method isEqualToString: @"TRACE"])
523+
{
524+
/* A TRACE must not have a body
525+
*/
526+
DESTROY(wData);
527+
cl = 0;
528+
NSMapRemove(wProperties, (void*)@"Content-Length");
529+
}
530+
531+
532+
/* When we have a non-empty body or a method which requires
533+
* a body, we must specify the content length.
505534
*/
506535
if ((id)NSMapGet(wProperties, (void*)@"Content-Length") == nil)
507536
{
508-
NSMapInsert(wProperties, (void*)@"Content-Length",
509-
(void*)[NSString stringWithFormat: @"%"PRIuPTR, [wData length]]);
537+
if (cl > 0
538+
|| [method isEqualToString: @"POST"]
539+
|| [method isEqualToString: @"PUT"]
540+
|| [method isEqualToString: @"PATCH"])
541+
{
542+
NSMapInsert(wProperties, (void*)@"Content-Length",
543+
(void*)[NSString stringWithFormat: @"%"PRIuPTR, cl]);
544+
}
510545
}
511546

512-
if ([wData length] > 0)
547+
if (cl > 0)
513548
{
514549
/*
515550
* Assume content type if not specified.
@@ -536,7 +571,6 @@ - (void) bgdApply: (NSString*)basic
536571
NSString *auth;
537572
GSHTTPAuthentication *authentication;
538573
NSURLCredential *cred;
539-
NSString *method;
540574
NSString *path;
541575

542576
/* Create credential from user and password stored in the URL.
@@ -562,19 +596,6 @@ - (void) bgdApply: (NSString*)basic
562596
RELEASE(cred);
563597
}
564598

565-
method = [request objectForKey: GSHTTPPropertyMethodKey];
566-
if (method == nil)
567-
{
568-
if ([wData length] > 0)
569-
{
570-
method = @"POST";
571-
}
572-
else
573-
{
574-
method = @"GET";
575-
}
576-
}
577-
578599
path = [u _requestPath: [[request
579600
objectForKey: GSHTTPPropertyDigestURIOmitsQuery] boolValue]];
580601

Source/NSURLProtocol.m

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1782,6 +1782,7 @@ - (void) stream: (NSStream*)stream handleEvent: (NSStreamEvent)event
17821782
NSDictionary *d;
17831783
NSEnumerator *e;
17841784
NSString *s;
1785+
NSString *method;
17851786
NSURL *u;
17861787
int l;
17871788
NSData *bytes;
@@ -1825,8 +1826,8 @@ - (void) stream: (NSStream*)stream handleEvent: (NSStreamEvent)event
18251826
* method /path;params?query HTTP/version
18261827
* where the params or query parts may be missing
18271828
*/
1828-
[m appendData: [[this->request HTTPMethod]
1829-
dataUsingEncoding: NSASCIIStringEncoding]];
1829+
method = [this->request HTTPMethod];
1830+
[m appendData: [method dataUsingEncoding: NSASCIIStringEncoding]];
18301831
[m appendBytes: " " length: 1];
18311832
u = [this->request URL];
18321833
s = [u _requestPath: 0];
@@ -1859,7 +1860,7 @@ - (void) stream: (NSStream*)stream handleEvent: (NSStreamEvent)event
18591860
* we therefore won't end up adding a second header by
18601861
* accident because the two header names differ in case.
18611862
*/
1862-
if ([[this->request HTTPMethod] isEqual: @"POST"]
1863+
if ([method isEqual: @"POST"]
18631864
&& [this->request valueForHTTPHeaderField:
18641865
@"Content-Type"] == nil)
18651866
{
@@ -1907,8 +1908,16 @@ - (void) stream: (NSStream*)stream handleEvent: (NSStreamEvent)event
19071908
[mm appendData: bytes];
19081909
}
19091910
}
1910-
if (l >= 0 && [this->request
1911-
valueForHTTPHeaderField: @"Content-Length"] == nil)
1911+
1912+
/* When we have a non-empty body or a method which requires
1913+
* a body, we must specify the content length.
1914+
*/
1915+
s = [this->request valueForHTTPHeaderField: @"Content-Length"];
1916+
if (nil == s
1917+
&& (l > 0
1918+
|| [method isEqualToString: @"POST"]
1919+
|| [method isEqualToString: @"PUT"]
1920+
|| [method isEqualToString: @"PATCH"]))
19121921
{
19131922
s = [NSString stringWithFormat: @"Content-Length: %d\r\n", l];
19141923
bytes = [s dataUsingEncoding: NSASCIIStringEncoding];

0 commit comments

Comments
 (0)