@@ -139,6 +139,7 @@ void HTTPClient::clear()
139139 _size = -1 ;
140140 _headers = " " ;
141141 _payload.reset ();
142+ _location = " " ;
142143}
143144
144145
@@ -217,7 +218,6 @@ bool HTTPClient::begin(String url, String httpsFingerprint)
217218 end ();
218219 }
219220
220- _port = 443 ;
221221 if (httpsFingerprint.length () == 0 ) {
222222 return false ;
223223 }
@@ -238,7 +238,6 @@ bool HTTPClient::begin(String url, const uint8_t httpsFingerprint[20])
238238 end ();
239239 }
240240
241- _port = 443 ;
242241 if (!beginInternal (url, " https" )) {
243242 return false ;
244243 }
@@ -264,7 +263,6 @@ bool HTTPClient::begin(String url)
264263 end ();
265264 }
266265
267- _port = 80 ;
268266 if (!beginInternal (url, " http" )) {
269267 return false ;
270268 }
@@ -288,6 +286,17 @@ bool HTTPClient::beginInternal(String url, const char* expectedProtocol)
288286 _protocol = url.substring (0 , index);
289287 url.remove (0 , (index + 3 )); // remove http:// or https://
290288
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+
291300 index = url.indexOf (' /' );
292301 String host = url.substring (0 , index);
293302 url.remove (0 , index); // remove host part
@@ -312,7 +321,7 @@ bool HTTPClient::beginInternal(String url, const char* expectedProtocol)
312321 }
313322 _uri = url;
314323
315- if (_protocol != expectedProtocol) {
324+ if ( expectedProtocol != nullptr && _protocol != expectedProtocol) {
316325 DEBUG_HTTPCLIENT (" [HTTP-Client][begin] unexpected protocol: %s, expected %s\n " , _protocol.c_str (), expectedProtocol);
317326 return false ;
318327 }
@@ -402,13 +411,14 @@ void HTTPClient::end(void)
402411{
403412 disconnect ();
404413 clear ();
414+ _redirectCount = 0 ;
405415}
406416
407417/* *
408418 * disconnect
409419 * close the TCP socket
410420 */
411- void HTTPClient::disconnect ()
421+ void HTTPClient::disconnect (bool preserveClient )
412422{
413423 if (connected ()) {
414424 if (_client->available () > 0 ) {
@@ -424,7 +434,9 @@ void HTTPClient::disconnect()
424434 DEBUG_HTTPCLIENT (" [HTTP-Client][end] tcp stop\n " );
425435 if (_client) {
426436 _client->stop ();
427- _client = nullptr ;
437+ if (!preserveClient) {
438+ _client = nullptr ;
439+ }
428440 }
429441#if HTTPCLIENT_1_1_COMPATIBLE
430442 if (_tcpDeprecated) {
@@ -507,6 +519,43 @@ void HTTPClient::setTimeout(uint16_t timeout)
507519 }
508520}
509521
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+
510559/* *
511560 * use HTTP1.0
512561 * @param timeout
@@ -589,29 +638,82 @@ int HTTPClient::sendRequest(const char * type, String payload)
589638 */
590639int HTTPClient::sendRequest (const char * type, uint8_t * payload, size_t size)
591640{
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+ }
596650
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);
600653
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+ }
605658
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" );
610712 }
611713 }
612714
613715 // handle Server Response (Header)
614- return returnError (handleHeaderResponse () );
716+ return returnError (code );
615717}
616718
617719/* *
@@ -762,6 +864,14 @@ int HTTPClient::getSize(void)
762864 return _size;
763865}
764866
867+ /* *
868+ * Location if redirect
869+ */
870+ const String& HTTPClient::getLocation (void )
871+ {
872+ return _location;
873+ }
874+
765875/* *
766876 * returns the stream of the tcp connection
767877 * @return WiFiClient
@@ -1173,6 +1283,10 @@ int HTTPClient::handleHeaderResponse()
11731283 transferEncoding = headerValue;
11741284 }
11751285
1286+ if (headerName.equalsIgnoreCase (" Location" )) {
1287+ _location = headerValue;
1288+ }
1289+
11761290 for (size_t i = 0 ; i < _headerKeysCount; i++) {
11771291 if (_currentHeaders[i].key .equalsIgnoreCase (headerName)) {
11781292 if (_currentHeaders[i].value != " " ) {
0 commit comments