@@ -139,6 +139,7 @@ void HTTPClient::clear()
139
139
_size = -1 ;
140
140
_headers = " " ;
141
141
_payload.reset ();
142
+ _location = " " ;
142
143
}
143
144
144
145
@@ -217,7 +218,6 @@ bool HTTPClient::begin(String url, String httpsFingerprint)
217
218
end ();
218
219
}
219
220
220
- _port = 443 ;
221
221
if (httpsFingerprint.length () == 0 ) {
222
222
return false ;
223
223
}
@@ -238,7 +238,6 @@ bool HTTPClient::begin(String url, const uint8_t httpsFingerprint[20])
238
238
end ();
239
239
}
240
240
241
- _port = 443 ;
242
241
if (!beginInternal (url, " https" )) {
243
242
return false ;
244
243
}
@@ -264,7 +263,6 @@ bool HTTPClient::begin(String url)
264
263
end ();
265
264
}
266
265
267
- _port = 80 ;
268
266
if (!beginInternal (url, " http" )) {
269
267
return false ;
270
268
}
@@ -288,6 +286,17 @@ bool HTTPClient::beginInternal(String url, const char* expectedProtocol)
288
286
_protocol = url.substring (0 , index );
289
287
url.remove (0 , (index + 3 )); // remove http:// or https://
290
288
289
+ if (_protocol == " http" ) {
290
+ // set default port for 'http'
291
+ _port = 80 ;
292
+ } else if (_protocol == " https" ) {
293
+ // set default port for 'https'
294
+ _port = 443 ;
295
+ } else {
296
+ DEBUG_HTTPCLIENT (" [HTTP-Client][begin] unsupported protocol: %s\n " , _protocol.c_str ());
297
+ return false ;
298
+ }
299
+
291
300
index = url.indexOf (' /' );
292
301
String host = url.substring (0 , index );
293
302
url.remove (0 , index ); // remove host part
@@ -312,7 +321,7 @@ bool HTTPClient::beginInternal(String url, const char* expectedProtocol)
312
321
}
313
322
_uri = url;
314
323
315
- if (_protocol != expectedProtocol) {
324
+ if ( expectedProtocol != nullptr && _protocol != expectedProtocol) {
316
325
DEBUG_HTTPCLIENT (" [HTTP-Client][begin] unexpected protocol: %s, expected %s\n " , _protocol.c_str (), expectedProtocol);
317
326
return false ;
318
327
}
@@ -402,13 +411,14 @@ void HTTPClient::end(void)
402
411
{
403
412
disconnect ();
404
413
clear ();
414
+ _redirectCount = 0 ;
405
415
}
406
416
407
417
/* *
408
418
* disconnect
409
419
* close the TCP socket
410
420
*/
411
- void HTTPClient::disconnect ()
421
+ void HTTPClient::disconnect (bool preserveClient )
412
422
{
413
423
if (connected ()) {
414
424
if (_client->available () > 0 ) {
@@ -424,7 +434,9 @@ void HTTPClient::disconnect()
424
434
DEBUG_HTTPCLIENT (" [HTTP-Client][end] tcp stop\n " );
425
435
if (_client) {
426
436
_client->stop ();
427
- _client = nullptr ;
437
+ if (!preserveClient) {
438
+ _client = nullptr ;
439
+ }
428
440
}
429
441
#if HTTPCLIENT_1_1_COMPATIBLE
430
442
if (_tcpDeprecated) {
@@ -507,6 +519,43 @@ void HTTPClient::setTimeout(uint16_t timeout)
507
519
}
508
520
}
509
521
522
+ /* *
523
+ * set the URL to a new value. Handy for following redirects.
524
+ * @param url
525
+ */
526
+ bool HTTPClient::setURL (String url)
527
+ {
528
+ // if the new location is only a path then only update the URI
529
+ if (_location.startsWith (" /" )) {
530
+ _uri = _location;
531
+ clear ();
532
+ return true ;
533
+ }
534
+
535
+ if (!url.startsWith (_protocol + " :" )) {
536
+ DEBUG_HTTPCLIENT (" [HTTP-Client][setURL] new URL not the same protocol, expected '%s', URL: '%s'\n " , _protocol.c_str (), url.c_str ());
537
+ return false ;
538
+ }
539
+ // disconnect but preserve _client
540
+ disconnect (true );
541
+ clear ();
542
+ return beginInternal (url, nullptr );
543
+ }
544
+
545
+ /* *
546
+ * set true to follow redirects.
547
+ * @param follow
548
+ */
549
+ void HTTPClient::setFollowRedirects (bool follow)
550
+ {
551
+ _followRedirects = follow;
552
+ }
553
+
554
+ void HTTPClient::setRedirectLimit (uint16_t limit)
555
+ {
556
+ _redirectLimit = limit;
557
+ }
558
+
510
559
/* *
511
560
* use HTTP1.0
512
561
* @param timeout
@@ -589,29 +638,82 @@ int HTTPClient::sendRequest(const char * type, String payload)
589
638
*/
590
639
int HTTPClient::sendRequest (const char * type, uint8_t * payload, size_t size)
591
640
{
592
- // connect to server
593
- if (!connect ()) {
594
- return returnError (HTTPC_ERROR_CONNECTION_REFUSED);
595
- }
641
+ bool redirect = false ;
642
+ int code = 0 ;
643
+ do {
644
+ // wipe out any existing headers from previous request
645
+ for (size_t i = 0 ; i < _headerKeysCount; i++) {
646
+ if (_currentHeaders[i].value .length () > 0 ) {
647
+ _currentHeaders[i].value = " " ;
648
+ }
649
+ }
596
650
597
- if (payload && size > 0 ) {
598
- addHeader (F (" Content-Length" ), String (size));
599
- }
651
+ redirect = false ;
652
+ DEBUG_HTTPCLIENT (" [HTTP-Client][sendRequest] type: '%s' redirCount: %d\n " , type, _redirectCount);
600
653
601
- // send Header
602
- if (!sendHeader (type )) {
603
- return returnError (HTTPC_ERROR_SEND_HEADER_FAILED );
604
- }
654
+ // connect to server
655
+ if (!connect ( )) {
656
+ return returnError (HTTPC_ERROR_CONNECTION_REFUSED );
657
+ }
605
658
606
- // send Payload if needed
607
- if (payload && size > 0 ) {
608
- if (_client->write (&payload[0 ], size) != size) {
609
- return returnError (HTTPC_ERROR_SEND_PAYLOAD_FAILED);
659
+ if (payload && size > 0 ) {
660
+ addHeader (F (" Content-Length" ), String (size));
661
+ }
662
+
663
+ // send Header
664
+ if (!sendHeader (type)) {
665
+ return returnError (HTTPC_ERROR_SEND_HEADER_FAILED);
666
+ }
667
+
668
+ // send Payload if needed
669
+ if (payload && size > 0 ) {
670
+ if (_client->write (&payload[0 ], size) != size) {
671
+ return returnError (HTTPC_ERROR_SEND_PAYLOAD_FAILED);
672
+ }
673
+ }
674
+
675
+ // handle Server Response (Header)
676
+ code = handleHeaderResponse ();
677
+
678
+ //
679
+ // We can follow redirects for 301/302/307 for GET and HEAD requests and
680
+ // and we have not exceeded the redirect limit preventing an infinite
681
+ // redirect loop.
682
+ //
683
+ // https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
684
+ //
685
+ if (_followRedirects &&
686
+ (_redirectCount < _redirectLimit) &&
687
+ (_location.length () > 0 ) &&
688
+ (code == 301 || code == 302 || code == 307 ) &&
689
+ (!strcmp (type, " GET" ) || !strcmp (type, " HEAD" ))
690
+ ) {
691
+ _redirectCount += 1 ; // increment the count for redirect.
692
+ redirect = true ;
693
+ DEBUG_HTTPCLIENT (" [HTTP-Client][sendRequest] following redirect:: '%s' redirCount: %d\n " , _location.c_str (), _redirectCount);
694
+ if (!setURL (_location)) {
695
+ // return the redirect instead of handling on failure of setURL()
696
+ redirect = false ;
697
+ }
698
+ }
699
+
700
+ } while (redirect);
701
+
702
+ // handle 303 redirect for non GET/HEAD by changing to GET and requesting new url
703
+ if (_followRedirects &&
704
+ (_redirectCount < _redirectLimit) &&
705
+ (_location.length () > 0 ) &&
706
+ (code == 303 ) &&
707
+ strcmp (type, " GET" ) && strcmp (type, " HEAD" )
708
+ ) {
709
+ _redirectCount += 1 ;
710
+ if (setURL (_location)) {
711
+ code = sendRequest (" GET" );
610
712
}
611
713
}
612
714
613
715
// handle Server Response (Header)
614
- return returnError (handleHeaderResponse () );
716
+ return returnError (code );
615
717
}
616
718
617
719
/* *
@@ -762,6 +864,14 @@ int HTTPClient::getSize(void)
762
864
return _size;
763
865
}
764
866
867
+ /* *
868
+ * Location if redirect
869
+ */
870
+ const String& HTTPClient::getLocation (void )
871
+ {
872
+ return _location;
873
+ }
874
+
765
875
/* *
766
876
* returns the stream of the tcp connection
767
877
* @return WiFiClient
@@ -1173,6 +1283,10 @@ int HTTPClient::handleHeaderResponse()
1173
1283
transferEncoding = headerValue;
1174
1284
}
1175
1285
1286
+ if (headerName.equalsIgnoreCase (" Location" )) {
1287
+ _location = headerValue;
1288
+ }
1289
+
1176
1290
for (size_t i = 0 ; i < _headerKeysCount; i++) {
1177
1291
if (_currentHeaders[i].key .equalsIgnoreCase (headerName)) {
1178
1292
if (_currentHeaders[i].value != " " ) {
0 commit comments