github.com/jkawamoto/roadie-azure@v0.3.5/roadie/url.go (about)

     1  //
     2  // roadie/url.go
     3  //
     4  // Copyright (c) 2017 Junpei Kawamoto
     5  //
     6  // This file is part of Roadie Azure.
     7  //
     8  // Roadie Azure is free software: you can redistribute it and/or modify
     9  // it under the terms of the GNU General Public License as published by
    10  // the Free Software Foundation, either version 3 of the License, or
    11  // (at your option) any later version.
    12  //
    13  // Roadie Azure is distributed in the hope that it will be useful,
    14  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    15  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    16  // GNU General Public License for more details.
    17  //
    18  // You should have received a copy of the GNU General Public License
    19  // along with Roadie Azure. If not, see <http://www.gnu.org/licenses/>.
    20  //
    21  
    22  package roadie
    23  
    24  import (
    25  	"compress/gzip"
    26  	"context"
    27  	"io"
    28  	"net/http"
    29  	"net/url"
    30  	"path"
    31  	"path/filepath"
    32  	"regexp"
    33  	"strings"
    34  
    35  	"golang.org/x/net/context/ctxhttp"
    36  )
    37  
    38  var (
    39  	// RegexpContentDisposition is a regular expression to obtain a file name
    40  	// from Content-Disposition header.
    41  	RegexpContentDisposition = regexp.MustCompile(`filename="?([^"]+)"?;?`)
    42  )
    43  
    44  // Object representing a file in a web server.
    45  type Object struct {
    46  	// Response is a raw response from a http server.
    47  	Response *http.Response
    48  	// Name of this object.
    49  	Name string
    50  	// Destination where this object should be stored.
    51  	Dest string
    52  	// Body is the stream of content body.
    53  	Body io.ReadCloser
    54  }
    55  
    56  // OpenURL opens a given url and returns an object associated with it.
    57  func OpenURL(ctx context.Context, u string) (obj *Object, err error) {
    58  
    59  	loc, err := url.Parse(u)
    60  	if err != nil {
    61  		return
    62  	}
    63  
    64  	comps := filepath.SplitList(loc.Path)
    65  	var dest, name string
    66  	if len(comps) != 1 {
    67  		loc.Path = comps[0]
    68  		dest = path.Dir(comps[1])
    69  		if !strings.HasSuffix(comps[1], "/") {
    70  			name = path.Base(comps[1])
    71  		}
    72  	}
    73  
    74  	if loc.Scheme == "dropbox" {
    75  		loc = expandDropboxURL(loc)
    76  	}
    77  
    78  	req, err := http.NewRequest(http.MethodGet, loc.String(), nil)
    79  	if err != nil {
    80  		return
    81  	}
    82  	req.Header.Add("Accept-encoding", "gzip")
    83  
    84  	res, err := ctxhttp.Do(ctx, nil, req)
    85  	if err != nil {
    86  		return
    87  	}
    88  
    89  	body := res.Body
    90  	if res.Header.Get("Content-Encoding") == "gzip" {
    91  		body, err = gzip.NewReader(body)
    92  		if err != nil {
    93  			return
    94  		}
    95  	}
    96  
    97  	// Name is the base of the url but if Content-Disposition header is given,
    98  	// use that value instead.
    99  	if name == "" {
   100  		if disposition := res.Header.Get("Content-Disposition"); disposition != "" {
   101  			if match := RegexpContentDisposition.FindStringSubmatch(disposition); match != nil {
   102  				name = match[1]
   103  			}
   104  		}
   105  		if name == "" {
   106  			name = path.Base(loc.Path)
   107  		}
   108  	}
   109  
   110  	obj = &Object{
   111  		Response: res,
   112  		Name:     name,
   113  		Dest:     dest,
   114  		Body:     body,
   115  	}
   116  	return
   117  
   118  }
   119  
   120  // expandDropboxURL modifies a given URL which has dropbox schema.
   121  func expandDropboxURL(loc *url.URL) *url.URL {
   122  
   123  	loc.Scheme = "https"
   124  	loc.Path = path.Join("/", loc.Host, loc.Path)
   125  	loc.Host = "www.dropbox.com"
   126  	loc.RawQuery = "dl=1"
   127  	return loc
   128  
   129  }