| 
									
										
										
										
											2023-03-12 16:00:57 +01:00
										 |  |  | // GoToSocial | 
					
						
							|  |  |  | // Copyright (C) GoToSocial Authors admin@gotosocial.org | 
					
						
							|  |  |  | // SPDX-License-Identifier: AGPL-3.0-or-later | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // This program is free software: you can redistribute it and/or modify | 
					
						
							|  |  |  | // it under the terms of the GNU Affero General Public License as published by | 
					
						
							|  |  |  | // the Free Software Foundation, either version 3 of the License, or | 
					
						
							|  |  |  | // (at your option) any later version. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // This program is distributed in the hope that it will be useful, | 
					
						
							|  |  |  | // but WITHOUT ANY WARRANTY; without even the implied warranty of | 
					
						
							|  |  |  | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
					
						
							|  |  |  | // GNU Affero General Public License for more details. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // You should have received a copy of the GNU Affero General Public License | 
					
						
							|  |  |  | // along with this program.  If not, see <http://www.gnu.org/licenses/>. | 
					
						
							| 
									
										
										
										
											2021-08-25 15:34:33 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-17 19:06:58 +02:00
										 |  |  | package transport | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"context" | 
					
						
							|  |  |  | 	"crypto" | 
					
						
							| 
									
										
										
										
											2022-05-15 10:16:43 +01:00
										 |  |  | 	"errors" | 
					
						
							| 
									
										
										
										
											2022-01-16 18:52:55 +01:00
										 |  |  | 	"io" | 
					
						
							| 
									
										
										
										
											2022-05-15 10:16:43 +01:00
										 |  |  | 	"net/http" | 
					
						
							| 
									
										
										
										
											2021-05-17 19:06:58 +02:00
										 |  |  | 	"net/url" | 
					
						
							|  |  |  | 	"sync" | 
					
						
							| 
									
										
										
										
											2022-05-15 10:16:43 +01:00
										 |  |  | 	"time" | 
					
						
							| 
									
										
										
										
											2021-05-17 19:06:58 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	"github.com/go-fed/httpsig" | 
					
						
							| 
									
										
										
										
											2023-04-28 16:45:21 +01:00
										 |  |  | 	"github.com/superseriousbusiness/gotosocial/internal/gtscontext" | 
					
						
							| 
									
										
										
										
											2021-06-27 16:52:18 +02:00
										 |  |  | 	"github.com/superseriousbusiness/gotosocial/internal/gtsmodel" | 
					
						
							| 
									
										
										
										
											2022-05-26 13:38:41 +02:00
										 |  |  | 	"github.com/superseriousbusiness/gotosocial/internal/httpclient" | 
					
						
							| 
									
										
										
										
											2021-05-17 19:06:58 +02:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-23 22:40:07 +01:00
										 |  |  | // Transport implements the pub.Transport interface with some additional functionality for fetching remote media. | 
					
						
							| 
									
										
										
										
											2022-03-15 15:01:19 +01:00
										 |  |  | // | 
					
						
							|  |  |  | // Since the transport has the concept of 'shortcuts' for fetching data locally rather than remotely, it is | 
					
						
							|  |  |  | // not *always* the case that calling a Transport function does an http call, but it usually will for remote | 
					
						
							|  |  |  | // hosts or resources for which a shortcut isn't provided by the transport controller (also in this package). | 
					
						
							| 
									
										
										
										
											2022-11-23 22:40:07 +01:00
										 |  |  | // | 
					
						
							|  |  |  | // For any of the transport functions, if a Fastfail context is passed in as the first parameter, the function | 
					
						
							|  |  |  | // will return after the first transport failure, instead of retrying + backing off. | 
					
						
							| 
									
										
										
										
											2021-05-17 19:06:58 +02:00
										 |  |  | type Transport interface { | 
					
						
							| 
									
										
										
										
											2022-11-23 22:40:07 +01:00
										 |  |  | 	/* | 
					
						
							|  |  |  | 		POST functions | 
					
						
							|  |  |  | 	*/ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-27 15:02:52 +01:00
										 |  |  | 	// POST will perform given the http request using | 
					
						
							|  |  |  | 	// transport client, retrying on certain preset errors. | 
					
						
							|  |  |  | 	POST(*http.Request, []byte) (*http.Response, error) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-23 22:40:07 +01:00
										 |  |  | 	// Deliver sends an ActivityStreams object. | 
					
						
							|  |  |  | 	Deliver(ctx context.Context, b []byte, to *url.URL) error | 
					
						
							| 
									
										
										
										
											2023-03-06 09:38:43 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-23 22:40:07 +01:00
										 |  |  | 	// BatchDeliver sends an ActivityStreams object to multiple recipients. | 
					
						
							|  |  |  | 	BatchDeliver(ctx context.Context, b []byte, recipients []*url.URL) error | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* | 
					
						
							|  |  |  | 		GET functions | 
					
						
							|  |  |  | 	*/ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-27 15:02:52 +01:00
										 |  |  | 	// GET will perform the given http request using | 
					
						
							|  |  |  | 	// transport client, retrying on certain preset errors. | 
					
						
							|  |  |  | 	GET(*http.Request) (*http.Response, error) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-23 22:40:07 +01:00
										 |  |  | 	// Dereference fetches the ActivityStreams object located at this IRI with a GET request. | 
					
						
							|  |  |  | 	Dereference(ctx context.Context, iri *url.URL) ([]byte, error) | 
					
						
							| 
									
										
										
										
											2023-03-06 09:38:43 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-01-23 14:41:58 +01:00
										 |  |  | 	// DereferenceMedia fetches the given media attachment IRI, returning the reader and filesize. | 
					
						
							| 
									
										
										
										
											2022-09-29 21:50:43 +01:00
										 |  |  | 	DereferenceMedia(ctx context.Context, iri *url.URL) (io.ReadCloser, int64, error) | 
					
						
							| 
									
										
										
										
											2023-03-06 09:38:43 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-27 16:52:18 +02:00
										 |  |  | 	// DereferenceInstance dereferences remote instance information, first by checking /api/v1/instance, and then by checking /.well-known/nodeinfo. | 
					
						
							| 
									
										
										
										
											2021-08-25 15:34:33 +02:00
										 |  |  | 	DereferenceInstance(ctx context.Context, iri *url.URL) (*gtsmodel.Instance, error) | 
					
						
							| 
									
										
										
										
											2023-03-06 09:38:43 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-29 19:36:54 +02:00
										 |  |  | 	// Finger performs a webfinger request with the given username and domain, and returns the bytes from the response body. | 
					
						
							| 
									
										
										
										
											2022-11-23 22:40:07 +01:00
										 |  |  | 	Finger(ctx context.Context, targetUsername string, targetDomain string) ([]byte, error) | 
					
						
							| 
									
										
										
										
											2021-05-17 19:06:58 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-28 16:45:21 +01:00
										 |  |  | // transport implements the Transport interface. | 
					
						
							| 
									
										
										
										
											2021-05-17 19:06:58 +02:00
										 |  |  | type transport struct { | 
					
						
							| 
									
										
										
										
											2022-05-15 10:16:43 +01:00
										 |  |  | 	controller *controller | 
					
						
							|  |  |  | 	pubKeyID   string | 
					
						
							|  |  |  | 	privkey    crypto.PrivateKey | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	signerExp  time.Time | 
					
						
							|  |  |  | 	getSigner  httpsig.Signer | 
					
						
							|  |  |  | 	postSigner httpsig.Signer | 
					
						
							|  |  |  | 	signerMu   sync.Mutex | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-02-10 20:16:01 +00:00
										 |  |  | func (t *transport) GET(r *http.Request) (*http.Response, error) { | 
					
						
							| 
									
										
										
										
											2022-05-15 10:16:43 +01:00
										 |  |  | 	if r.Method != http.MethodGet { | 
					
						
							|  |  |  | 		return nil, errors.New("must be GET request") | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2023-04-28 16:45:21 +01:00
										 |  |  | 	ctx := r.Context() // extract, set pubkey ID. | 
					
						
							| 
									
										
										
										
											2023-06-13 16:47:56 +02:00
										 |  |  | 	ctx = gtscontext.SetOutgoingPublicKeyID(ctx, t.pubKeyID) | 
					
						
							| 
									
										
										
										
											2023-04-28 16:45:21 +01:00
										 |  |  | 	r = r.WithContext(ctx) // replace request ctx. | 
					
						
							|  |  |  | 	r.Header.Set("User-Agent", t.controller.userAgent) | 
					
						
							|  |  |  | 	return t.controller.client.DoSigned(r, t.signGET()) | 
					
						
							| 
									
										
										
										
											2022-05-15 10:16:43 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-02-10 20:16:01 +00:00
										 |  |  | func (t *transport) POST(r *http.Request, body []byte) (*http.Response, error) { | 
					
						
							| 
									
										
										
										
											2022-05-15 10:16:43 +01:00
										 |  |  | 	if r.Method != http.MethodPost { | 
					
						
							|  |  |  | 		return nil, errors.New("must be POST request") | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2023-04-28 16:45:21 +01:00
										 |  |  | 	ctx := r.Context() // extract, set pubkey ID. | 
					
						
							| 
									
										
										
										
											2023-06-13 16:47:56 +02:00
										 |  |  | 	ctx = gtscontext.SetOutgoingPublicKeyID(ctx, t.pubKeyID) | 
					
						
							| 
									
										
										
										
											2023-04-28 16:45:21 +01:00
										 |  |  | 	r = r.WithContext(ctx) // replace request ctx. | 
					
						
							| 
									
										
										
										
											2022-11-26 21:19:42 +01:00
										 |  |  | 	r.Header.Set("User-Agent", t.controller.userAgent) | 
					
						
							| 
									
										
										
										
											2023-04-28 16:45:21 +01:00
										 |  |  | 	return t.controller.client.DoSigned(r, t.signPOST(body)) | 
					
						
							| 
									
										
										
										
											2022-05-15 10:16:43 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // signGET will safely sign an HTTP GET request. | 
					
						
							| 
									
										
										
										
											2023-04-28 16:45:21 +01:00
										 |  |  | func (t *transport) signGET() httpclient.SignFunc { | 
					
						
							|  |  |  | 	return func(r *http.Request) (err error) { | 
					
						
							|  |  |  | 		t.safesign(func() { | 
					
						
							|  |  |  | 			err = t.getSigner.SignRequest(t.privkey, t.pubKeyID, r, nil) | 
					
						
							|  |  |  | 		}) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2022-05-15 10:16:43 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // signPOST will safely sign an HTTP POST request for given body. | 
					
						
							| 
									
										
										
										
											2023-04-28 16:45:21 +01:00
										 |  |  | func (t *transport) signPOST(body []byte) httpclient.SignFunc { | 
					
						
							|  |  |  | 	return func(r *http.Request) (err error) { | 
					
						
							|  |  |  | 		t.safesign(func() { | 
					
						
							|  |  |  | 			err = t.postSigner.SignRequest(t.privkey, t.pubKeyID, r, body) | 
					
						
							|  |  |  | 		}) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2022-05-15 10:16:43 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // safesign will perform sign function within mutex protection, | 
					
						
							|  |  |  | // and ensured that httpsig.Signers are up-to-date. | 
					
						
							|  |  |  | func (t *transport) safesign(sign func()) { | 
					
						
							|  |  |  | 	// Perform within mu safety | 
					
						
							|  |  |  | 	t.signerMu.Lock() | 
					
						
							|  |  |  | 	defer t.signerMu.Unlock() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if now := time.Now(); now.After(t.signerExp) { | 
					
						
							|  |  |  | 		const expiry = 120 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// Signers have expired and require renewal | 
					
						
							|  |  |  | 		t.getSigner, _ = NewGETSigner(expiry) | 
					
						
							|  |  |  | 		t.postSigner, _ = NewPOSTSigner(expiry) | 
					
						
							|  |  |  | 		t.signerExp = now.Add(time.Second * expiry) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Perform signing | 
					
						
							|  |  |  | 	sign() | 
					
						
							| 
									
										
										
										
											2022-03-15 15:01:19 +01:00
										 |  |  | } |