@@ -25,49 +25,77 @@ type ErrorLogger interface {
2525// HttpIpfs is an http.Handler that serves IPLD data via HTTP according to the
2626// Trustless Gateway specification.
2727type HttpIpfs struct {
28- ctx context.Context
29- logWriter io.Writer
30- lsys linking.LinkSystem
31- maxResponseDuration time.Duration
32- maxResponseBytes int64
28+ ctx context.Context
29+ lsys linking.LinkSystem
30+ cfg * httpOptions
31+ }
32+
33+ type httpOptions struct {
34+ MaxResponseDuration time.Duration
35+ MaxResponseBytes int64
36+ }
37+
38+ type HttpOption func (* httpOptions )
39+
40+ // WithMaxResponseDuration sets the maximum duration for a response to be
41+ // streamed before the connection is closed. This allows a server to limit the
42+ // amount of time a client can hold a connection open; and also restricts the
43+ // ability to serve very large DAGs.
44+ //
45+ // A value of 0 will disable the limitation. This is the default.
46+ func WithMaxResponseDuration (d time.Duration ) HttpOption {
47+ return func (o * httpOptions ) {
48+ o .MaxResponseDuration = d
49+ }
50+ }
51+
52+ // WithMaxResponseBytes sets the maximum number of bytes that will be streamed
53+ // before the connection is closed. This allows a server to limit the amount of
54+ // data a client can request; and also restricts the ability to serve very large
55+ // DAGs.
56+ //
57+ // A value of 0 will disable the limitation. This is the default.
58+ func WithMaxResponseBytes (b int64 ) HttpOption {
59+ return func (o * httpOptions ) {
60+ o .MaxResponseBytes = b
61+ }
3362}
3463
3564func NewHttpIpfs (
3665 ctx context.Context ,
37- logWriter io.Writer ,
3866 lsys linking.LinkSystem ,
39- maxResponseDuration time.Duration ,
40- maxResponseBytes int64 ,
67+ opts ... HttpOption ,
4168) * HttpIpfs {
69+ cfg := & httpOptions {}
70+ for _ , opt := range opts {
71+ opt (cfg )
72+ }
4273
4374 return & HttpIpfs {
44- ctx : ctx ,
45- logWriter : logWriter ,
46- lsys : lsys ,
47- maxResponseDuration : maxResponseDuration ,
48- maxResponseBytes : maxResponseBytes ,
75+ ctx : ctx ,
76+ lsys : lsys ,
77+ cfg : cfg ,
4978 }
5079}
5180
5281func (hi * HttpIpfs ) ServeHTTP (res http.ResponseWriter , req * http.Request ) {
5382 ctx := hi .ctx
54- if hi .maxResponseDuration > 0 {
83+ if hi .cfg . MaxResponseDuration > 0 {
5584 var cancel context.CancelFunc
56- ctx , cancel = context .WithTimeout (ctx , hi .maxResponseDuration )
85+ ctx , cancel = context .WithTimeout (ctx , hi .cfg . MaxResponseDuration )
5786 defer cancel ()
5887 }
5988
6089 logError := func (status int , err error ) {
90+ res .WriteHeader (status )
91+ res .Write ([]byte (err .Error ()))
6192 if lrw , ok := res .(ErrorLogger ); ok {
6293 lrw .LogError (status , err )
6394 } else {
64- logger .Debug ("Error handling request from [%s] for [%s] status=%d, msg=%s" , req .RemoteAddr , req .URL , status , err .Error ())
95+ logger .Debugf ("Error handling request from [%s] for [%s] status=%d, msg=%s" , req .RemoteAddr , req .URL , status , err .Error ())
6596 }
6697 }
6798
68- path := datamodel .ParsePath (req .URL .Path )
69- _ , path = path .Shift () // remove /ipfs
70-
7199 // filter out everything but GET requests
72100 switch req .Method {
73101 case http .MethodGet :
@@ -78,6 +106,9 @@ func (hi *HttpIpfs) ServeHTTP(res http.ResponseWriter, req *http.Request) {
78106 return
79107 }
80108
109+ path := datamodel .ParsePath (req .URL .Path )
110+ _ , path = path .Shift () // remove /ipfs
111+
81112 // check if CID path param is missing
82113 if path .Len () == 0 {
83114 // not a valid path to hit
@@ -102,7 +133,7 @@ func (hi *HttpIpfs) ServeHTTP(res http.ResponseWriter, req *http.Request) {
102133 cidSeg , path = path .Shift ()
103134 rootCid , err := cid .Parse (cidSeg .String ())
104135 if err != nil {
105- logError (http .StatusInternalServerError , errors .New ("failed to parse CID path parameter" ))
136+ logError (http .StatusBadRequest , errors .New ("failed to parse CID path parameter" ))
106137 return
107138 }
108139
@@ -131,7 +162,7 @@ func (hi *HttpIpfs) ServeHTTP(res http.ResponseWriter, req *http.Request) {
131162 }
132163
133164 bytesWrittenCh := make (chan struct {})
134- writer := newIpfsResponseWriter (res , hi .maxResponseBytes , func () {
165+ writer := newIpfsResponseWriter (res , hi .cfg . MaxResponseBytes , func () {
135166 // called once we start writing blocks into the CAR (on the first Put())
136167 res .Header ().Set ("Content-Disposition" , fmt .Sprintf ("attachment; filename=%q" , fileName ))
137168 res .Header ().Set ("Cache-Control" , trustlesshttp .ResponseCacheControlHeader )
0 commit comments