<commit_before>//
// store.go
//

package srnd

import (
  "github.com/majestrate/srndv2/src/nacl"
  "bufio"
  "bytes"
  "crypto/sha512"
  "errors"
  "io"
  "log"
  "mime"
  "mime/multipart"
  "net/mail"
  "os"
  "os/exec"
  "path/filepath"
  "strings"
)

type ArticleStore interface {
  MessageReader
  MessageWriter

  // get the filepath for an attachment
  AttachmentFilepath(fname string) string
  // get the filepath for an attachment's thumbnail
  ThumbnailFilepath(fname string) string
  // do we have this article?
  HasArticle(msgid string) bool
  // create a file for a message
  CreateFile(msgid string) io.WriteCloser
  // create a file for a temp message, returns nil if it's already open
  CreateTempFile(msgid string) io.WriteCloser
  // get the filename of a message
  GetFilename(msgid string) string
  // get the filename of a temp message
  GetTempFilename(msgid string) string
  // Get a message given its messageid
  GetMessage(msgid string) NNTPMessage
  // get a temp message given its messageid
  // temp message is deleted once read
  ReadTempMessage(msgid string) NNTPMessage
  // store a post
  StorePost(nntp NNTPMessage) error
  // get article headers only
  GetHeaders(msgid string) ArticleHeaders
  // get our temp directory for articles
  TempDir() string
  // get a list of all the attachments we have
  GetAllAttachments() ([]string, error)
  // generate a thumbnail
  GenerateThumbnail(fname string) error
}
type articleStore struct {
  directory string
  temp string
  attachments string
  thumbs string
  database Database
  convert_path string
  ffmpeg_path string
  sox_path string
}

func createArticleStore(config map[string]string, database Database) ArticleStore {
  store := articleStore{
    directory: config["store_dir"],
    temp: config["incoming_dir"],
    attachments: config["attachments_dir"],
    thumbs: config["thumbs_dir"],
    convert_path: config["convert_bin"],
    ffmpeg_path: config["ffmpegthumbnailer_bin"],
    sox_path: config["sox_bin"],
    database: database,
  }
  store.Init()
  return store
}

func (self articleStore) TempDir() string {
  return self.temp
}

// initialize article store
func (self articleStore) Init() {
  EnsureDir(self.directory)
  EnsureDir(self.temp)
  EnsureDir(self.attachments)
  EnsureDir(self.thumbs)
  if ! CheckFile(self.convert_path) {
    log.Fatal("cannot find executable for convert: ", self.convert_path, " not found")
  }
  if ! CheckFile(self.ffmpeg_path) {
    log.Fatal("connt find executable for ffmpegthumbnailer: ", self.ffmpeg_path, " not found")
  }
  if ! CheckFile(self.sox_path) {
    log.Fatal("connt find executable for sox: ", self.sox_path, " not found")
  }
}

func (self articleStore) isAudio(fname string) bool {
  for _, ext := range []string{".mp3", ".ogg", ".oga", ".opus", ".flac"} {
    if strings.HasSuffix(strings.ToLower(fname), ext) {
      return true
    }
  }
  return false
}

func (self articleStore) GenerateThumbnail(fname string) error {
  outfname := self.ThumbnailFilepath(fname)
  infname := self.AttachmentFilepath(fname)
  var cmd *exec.Cmd
  if strings.HasSuffix(strings.ToLower(fname), ".gif") {
    cmd = exec.Command(self.convert_path, "-thumbnail", "200", infname, outfname)
  } else if self.isAudio(fname) {
    cmd = exec.Command(self.sox_path, infname, "-n", "spectrogram", "-a", "-d", "0:30", "-r", "-p", "6", "-x", "200", "-y", "150", "-o", outfname)
  } else {
    cmd = exec.Command(self.ffmpeg_path, "-i", infname, "-o", outfname, "-s", "200")
  }
  exec_out, err := cmd.CombinedOutput()
  if err != nil {
    log.Println("error generating thumbnail", string(exec_out))
  }
  return err
}

func (self articleStore) GetAllAttachments() (names []string, err error) {
  var f *os.File
  f, err = os.Open(self.attachments)
  if err == nil {
    names, err = f.Readdirnames(0)
  }
  return
}

func (self articleStore) ReadMessage(r io.Reader) (NNTPMessage, error) {
  return read_message(r)
}

func (self articleStore) StorePost(nntp NNTPMessage) (err error) {

  f := self.CreateFile(nntp.MessageID())
  if f != nil {
    err = self.WriteMessage(nntp, f)
    f.Close()
  }

  nntp_inner := nntp.Signed()
  if nntp_inner == nil {
    // no inner article
    // store the data in the article
    self.database.RegisterArticle(nntp)
    for _, att := range nntp.Attachments() {
      // save attachments
      go self.saveAttachment(att)
    }
  } else {
    // record a tripcode
    self.database.RegisterSigned(nntp.MessageID(), nntp.Pubkey())
    // we have inner data
    // store the signed data
    self.database.RegisterArticle(nntp_inner)
    for _, att := range nntp_inner.Attachments() {
      go self.saveAttachment(att)
    }
  }
  return
}

// save an attachment
func (self articleStore) saveAttachment(att NNTPAttachment) {
  var err error
  var f io.WriteCloser
  fpath := att.Filepath()
  upload := self.AttachmentFilepath(fpath)
  thumb := self.ThumbnailFilepath(fpath)
  if CheckFile(upload) {
    log.Println("already have file", fpath)
    if ! CheckFile(thumb) && att.NeedsThumbnail() {
      log.Println("create thumbnail for", fpath)
      err = self.GenerateThumbnail(fpath)
      if err != nil {
        log.Println("failed to generate thumbnail", err)
      }
    }
    return
  }
  // save attachment
  log.Println("save attachment", att.Filename(), "to", upload)
  f, err = os.Create(upload)
  if err == nil {
    err = att.WriteTo(f)
    f.Close()
  }
  if err != nil {
    log.Println("did not save attachment", err)
    return
  }

  // generate thumbanils
  if att.NeedsThumbnail() {
    log.Println("create thumbnail for", fpath)
    err = self.GenerateThumbnail(fpath)
    if err != nil {
      log.Println("failed to generate thumbnail", err)
    }
  }
}

// eh this isn't really needed is it?
func (self articleStore) WriteMessage(nntp NNTPMessage, wr io.Writer) (err error) {
  return nntp.WriteTo(wr, "\n")
}

// get the filepath for an attachment
func (self articleStore) AttachmentFilepath(fname string) string {
  return filepath.Join(self.attachments, fname)
}

// get the filepath for a thumbanil
func (self articleStore) ThumbnailFilepath(fname string) string {
  // all thumbnails are jpegs now
  return filepath.Join(self.thumbs, fname + ".jpg")
}

// create a file for this article
func (self articleStore) CreateFile(messageID string) io.WriteCloser {
  fname := self.GetFilename(messageID)
  file, err := os.Create(fname)
  if err != nil {
    log.Println("cannot open file", fname)
    return nil
  }
  return file
}

// create a temp file for inboud articles
func (self articleStore) CreateTempFile(messageID string) io.WriteCloser {
  fname := self.GetTempFilename(messageID)
  if CheckFile(fname) {
    log.Println(fname, "already open")
    return nil
  }
  file, err := os.Create(fname)
  if err != nil {
    log.Println("cannot open file", fname)
    return nil
  }
  return file
}

// return true if we have an article
func (self articleStore) HasArticle(messageID string) bool {
  return CheckFile(self.GetFilename(messageID))
}

// get the filename for this article
func (self articleStore) GetFilename(messageID string) string {
  if ! ValidMessageID(messageID) {
    log.Println("!!! bug: tried to open invalid message", messageID, "!!!")
    return ""
  }
  return filepath.Join(self.directory, messageID)
}

// get the filename for this article
func (self articleStore) GetTempFilename(messageID string) string {
  if ! ValidMessageID(messageID) {
    log.Println("!!! bug: tried to open invalid temp message", messageID, "!!!")
    return ""
  }
  return filepath.Join(self.temp, messageID)
}

// loads temp message and deletes old article
func (self articleStore) ReadTempMessage(messageID string) NNTPMessage {
  fname := self.GetTempFilename(messageID)
  nntp := self.readfile(fname)
  DelFile(fname)
  return nntp
}

// read a file give filepath
func (self articleStore) readfile(fname string) NNTPMessage {

  file, err := os.Open(fname)
  if err != nil {
    log.Println("store cannot open file",fname)
    return nil
  }
  message, err := self.ReadMessage(file)
  file.Close()
  if err == nil {
    return message
  }

  log.Println("failed to load file", fname)
  return nil
}

// get the replies for a thread
func (self articleStore) GetThreadReplies(messageID string, last int) []NNTPMessage {
  var repls []NNTPMessage
  if self.database.ThreadHasReplies(messageID) {
    rpls := self.database.GetThreadReplies(messageID, last)
    if rpls == nil {
      return repls
    }
    for _, rpl := range rpls {
      msg := self.GetMessage(rpl)
      if msg == nil {
        log.Println("cannot get message", rpl)
      } else {
        repls = append(repls, msg)
      }
    }
  }
  return repls
}

// load an article
// return nil on failure
func (self articleStore) GetMessage(messageID string) NNTPMessage {
  return self.readfile(self.GetFilename(messageID))
}

// get article with headers only
func (self articleStore) GetHeaders(messageID string) ArticleHeaders {
  // TODO: don't load the entire body
  nntp := self.readfile(self.GetFilename(messageID))
  if nntp == nil {
    return nil
  }
  return nntp.Headers()
}

func read_message(r io.Reader) (NNTPMessage, error) {

  msg, err := mail.ReadMessage(r)
  var nntp nntpArticle

  if err == nil {
    nntp.headers = ArticleHeaders(msg.Header)
    content_type := nntp.ContentType()
    media_type, params, err := mime.ParseMediaType(content_type)
    if err != nil {
      log.Println("failed to parse media type", err, "for mime", content_type)
      return nil, err
    }
    boundary, ok := params["boundary"]
    if ok {
      partReader := multipart.NewReader(msg.Body, boundary)
      for {
        part, err := partReader.NextPart()
        if err == io.EOF {
          return nntp, nil
        } else if err == nil {
          hdr := part.Header
          // get content type of part
          part_type := hdr.Get("Content-Type")
          // parse content type
          media_type, _, err = mime.ParseMediaType(part_type)
          if err == nil {
            if media_type == "text/plain" {
              att := readAttachmentFromMimePart(part)
              if att != nil {
                nntp.message = att.(nntpAttachment)
                nntp.message.header.Set("Content-Type", part_type)
              }
            } else {
              // non plaintext gets added to attachments
              att := readAttachmentFromMimePart(part)
              if att != nil {
                nntp = nntp.Attach(att).(nntpArticle)
              }
            }
          } else {
            log.Println("part has no content type", err)
          }
          part.Close()
        } else {
          log.Println("failed to load part! ", err)
          return nil, err
        }
      }

    } else if media_type == "message/rfc822" {
      // tripcoded message
      sig := nntp.headers.Get("X-Signature-Ed25519-Sha512", "")
      pk := nntp.Pubkey()
      if pk == "" || sig == "" {
        log.Println("invalid sig or pubkey", sig, pk)
        return nil, errors.New("invalid headers")
      }
      log.Printf("got signed message from %s", pk)
      pk_bytes := unhex(pk)
      sig_bytes := unhex(sig)
      r := bufio.NewReader(msg.Body)
      crlf := []byte{13,10}
      for {
        line, err := r.ReadBytes('\n')
        if err == io.EOF {
          break
        }
        nntp.signedPart.body.Write(line[:len(line)-1])
        nntp.signedPart.body.Write(crlf)
      }
      if nntp.signedPart.body.Len() < 2 {
        log.Println("signed body is too small")
      } else {
        body := nntp.signedPart.body.Bytes()[:nntp.signedPart.body.Len()-2]
        body_hash := sha512.Sum512(body)
        log.Printf("hash=%s", hexify(body_hash[:]))
        if nacl.CryptoVerifyFucky(body_hash[:], sig_bytes, pk_bytes) {
          log.Println("signature is valid :^)")
          return nntp, nil
        } else {
          log.Println("!!!signature is invalid!!!")
        }
      }
    } else {
      // plaintext attachment
      var buff bytes.Buffer
      _, err = io.Copy(&buff, msg.Body)
      nntp.message = createPlaintextAttachment(buff.String())
      return nntp, err
    }
  } else {
    log.Println("failed to read message", err)
    return nil, err
  }
  return nntp, err
}

<commit_msg>check for "funky" images types and use convert instead of ffmpeg thumbnailer<commit_after>//
// store.go
//

package srnd

import (
  "github.com/majestrate/srndv2/src/nacl"
  "bufio"
  "bytes"
  "crypto/sha512"
  "errors"
  "io"
  "log"
  "mime"
  "mime/multipart"
  "net/mail"
  "os"
  "os/exec"
  "path/filepath"
  "strings"
)

type ArticleStore interface {
  MessageReader
  MessageWriter

  // get the filepath for an attachment
  AttachmentFilepath(fname string) string
  // get the filepath for an attachment's thumbnail
  ThumbnailFilepath(fname string) string
  // do we have this article?
  HasArticle(msgid string) bool
  // create a file for a message
  CreateFile(msgid string) io.WriteCloser
  // create a file for a temp message, returns nil if it's already open
  CreateTempFile(msgid string) io.WriteCloser
  // get the filename of a message
  GetFilename(msgid string) string
  // get the filename of a temp message
  GetTempFilename(msgid string) string
  // Get a message given its messageid
  GetMessage(msgid string) NNTPMessage
  // get a temp message given its messageid
  // temp message is deleted once read
  ReadTempMessage(msgid string) NNTPMessage
  // store a post
  StorePost(nntp NNTPMessage) error
  // get article headers only
  GetHeaders(msgid string) ArticleHeaders
  // get our temp directory for articles
  TempDir() string
  // get a list of all the attachments we have
  GetAllAttachments() ([]string, error)
  // generate a thumbnail
  GenerateThumbnail(fname string) error
}
type articleStore struct {
  directory string
  temp string
  attachments string
  thumbs string
  database Database
  convert_path string
  ffmpeg_path string
  sox_path string
}

func createArticleStore(config map[string]string, database Database) ArticleStore {
  store := articleStore{
    directory: config["store_dir"],
    temp: config["incoming_dir"],
    attachments: config["attachments_dir"],
    thumbs: config["thumbs_dir"],
    convert_path: config["convert_bin"],
    ffmpeg_path: config["ffmpegthumbnailer_bin"],
    sox_path: config["sox_bin"],
    database: database,
  }
  store.Init()
  return store
}

func (self articleStore) TempDir() string {
  return self.temp
}

// initialize article store
func (self articleStore) Init() {
  EnsureDir(self.directory)
  EnsureDir(self.temp)
  EnsureDir(self.attachments)
  EnsureDir(self.thumbs)
  if ! CheckFile(self.convert_path) {
    log.Fatal("cannot find executable for convert: ", self.convert_path, " not found")
  }
  if ! CheckFile(self.ffmpeg_path) {
    log.Fatal("connt find executable for ffmpegthumbnailer: ", self.ffmpeg_path, " not found")
  }
  if ! CheckFile(self.sox_path) {
    log.Fatal("connt find executable for sox: ", self.sox_path, " not found")
  }
}

func (self articleStore) isAudio(fname string) bool {
  for _, ext := range []string{".mp3", ".ogg", ".oga", ".opus", ".flac"} {
    if strings.HasSuffix(strings.ToLower(fname), ext) {
      return true
    }
  }
  return false
}
// is this an image format we need convert for?
func (self articleStore) isFunkyImage(fname string) bool {
  for _, ext := range []string{".gif", ".ico"} {
    if strings.HasSuffix(strings.ToLower(fname), ext) {
      return true
    }
  }
  return false

}

func (self articleStore) GenerateThumbnail(fname string) error {
  outfname := self.ThumbnailFilepath(fname)
  infname := self.AttachmentFilepath(fname)
  var cmd *exec.Cmd
  if self.isFunkyImage(fname)  {
    cmd = exec.Command(self.convert_path, "-thumbnail", "200", infname, outfname)
  } else if self.isAudio(fname) {
    cmd = exec.Command(self.sox_path, infname, "-n", "spectrogram", "-a", "-d", "0:30", "-r", "-p", "6", "-x", "200", "-y", "150", "-o", outfname)
  } else {
    cmd = exec.Command(self.ffmpeg_path, "-i", infname, "-o", outfname, "-s", "200")
  }
  exec_out, err := cmd.CombinedOutput()
  if err != nil {
    log.Println("error generating thumbnail", string(exec_out))
  }
  return err
}

func (self articleStore) GetAllAttachments() (names []string, err error) {
  var f *os.File
  f, err = os.Open(self.attachments)
  if err == nil {
    names, err = f.Readdirnames(0)
  }
  return
}

func (self articleStore) ReadMessage(r io.Reader) (NNTPMessage, error) {
  return read_message(r)
}

func (self articleStore) StorePost(nntp NNTPMessage) (err error) {

  f := self.CreateFile(nntp.MessageID())
  if f != nil {
    err = self.WriteMessage(nntp, f)
    f.Close()
  }

  nntp_inner := nntp.Signed()
  if nntp_inner == nil {
    // no inner article
    // store the data in the article
    self.database.RegisterArticle(nntp)
    for _, att := range nntp.Attachments() {
      // save attachments
      go self.saveAttachment(att)
    }
  } else {
    // record a tripcode
    self.database.RegisterSigned(nntp.MessageID(), nntp.Pubkey())
    // we have inner data
    // store the signed data
    self.database.RegisterArticle(nntp_inner)
    for _, att := range nntp_inner.Attachments() {
      go self.saveAttachment(att)
    }
  }
  return
}

// save an attachment
func (self articleStore) saveAttachment(att NNTPAttachment) {
  var err error
  var f io.WriteCloser
  fpath := att.Filepath()
  upload := self.AttachmentFilepath(fpath)
  thumb := self.ThumbnailFilepath(fpath)
  if CheckFile(upload) {
    log.Println("already have file", fpath)
    if ! CheckFile(thumb) && att.NeedsThumbnail() {
      log.Println("create thumbnail for", fpath)
      err = self.GenerateThumbnail(fpath)
      if err != nil {
        log.Println("failed to generate thumbnail", err)
      }
    }
    return
  }
  // save attachment
  log.Println("save attachment", att.Filename(), "to", upload)
  f, err = os.Create(upload)
  if err == nil {
    err = att.WriteTo(f)
    f.Close()
  }
  if err != nil {
    log.Println("did not save attachment", err)
    return
  }

  // generate thumbanils
  if att.NeedsThumbnail() {
    log.Println("create thumbnail for", fpath)
    err = self.GenerateThumbnail(fpath)
    if err != nil {
      log.Println("failed to generate thumbnail", err)
    }
  }
}

// eh this isn't really needed is it?
func (self articleStore) WriteMessage(nntp NNTPMessage, wr io.Writer) (err error) {
  return nntp.WriteTo(wr, "\n")
}

// get the filepath for an attachment
func (self articleStore) AttachmentFilepath(fname string) string {
  return filepath.Join(self.attachments, fname)
}

// get the filepath for a thumbanil
func (self articleStore) ThumbnailFilepath(fname string) string {
  // all thumbnails are jpegs now
  return filepath.Join(self.thumbs, fname + ".jpg")
}

// create a file for this article
func (self articleStore) CreateFile(messageID string) io.WriteCloser {
  fname := self.GetFilename(messageID)
  file, err := os.Create(fname)
  if err != nil {
    log.Println("cannot open file", fname)
    return nil
  }
  return file
}

// create a temp file for inboud articles
func (self articleStore) CreateTempFile(messageID string) io.WriteCloser {
  fname := self.GetTempFilename(messageID)
  if CheckFile(fname) {
    log.Println(fname, "already open")
    return nil
  }
  file, err := os.Create(fname)
  if err != nil {
    log.Println("cannot open file", fname)
    return nil
  }
  return file
}

// return true if we have an article
func (self articleStore) HasArticle(messageID string) bool {
  return CheckFile(self.GetFilename(messageID))
}

// get the filename for this article
func (self articleStore) GetFilename(messageID string) string {
  if ! ValidMessageID(messageID) {
    log.Println("!!! bug: tried to open invalid message", messageID, "!!!")
    return ""
  }
  return filepath.Join(self.directory, messageID)
}

// get the filename for this article
func (self articleStore) GetTempFilename(messageID string) string {
  if ! ValidMessageID(messageID) {
    log.Println("!!! bug: tried to open invalid temp message", messageID, "!!!")
    return ""
  }
  return filepath.Join(self.temp, messageID)
}

// loads temp message and deletes old article
func (self articleStore) ReadTempMessage(messageID string) NNTPMessage {
  fname := self.GetTempFilename(messageID)
  nntp := self.readfile(fname)
  DelFile(fname)
  return nntp
}

// read a file give filepath
func (self articleStore) readfile(fname string) NNTPMessage {

  file, err := os.Open(fname)
  if err != nil {
    log.Println("store cannot open file",fname)
    return nil
  }
  message, err := self.ReadMessage(file)
  file.Close()
  if err == nil {
    return message
  }

  log.Println("failed to load file", fname)
  return nil
}

// get the replies for a thread
func (self articleStore) GetThreadReplies(messageID string, last int) []NNTPMessage {
  var repls []NNTPMessage
  if self.database.ThreadHasReplies(messageID) {
    rpls := self.database.GetThreadReplies(messageID, last)
    if rpls == nil {
      return repls
    }
    for _, rpl := range rpls {
      msg := self.GetMessage(rpl)
      if msg == nil {
        log.Println("cannot get message", rpl)
      } else {
        repls = append(repls, msg)
      }
    }
  }
  return repls
}

// load an article
// return nil on failure
func (self articleStore) GetMessage(messageID string) NNTPMessage {
  return self.readfile(self.GetFilename(messageID))
}

// get article with headers only
func (self articleStore) GetHeaders(messageID string) ArticleHeaders {
  // TODO: don't load the entire body
  nntp := self.readfile(self.GetFilename(messageID))
  if nntp == nil {
    return nil
  }
  return nntp.Headers()
}

func read_message(r io.Reader) (NNTPMessage, error) {

  msg, err := mail.ReadMessage(r)
  var nntp nntpArticle

  if err == nil {
    nntp.headers = ArticleHeaders(msg.Header)
    content_type := nntp.ContentType()
    media_type, params, err := mime.ParseMediaType(content_type)
    if err != nil {
      log.Println("failed to parse media type", err, "for mime", content_type)
      return nil, err
    }
    boundary, ok := params["boundary"]
    if ok {
      partReader := multipart.NewReader(msg.Body, boundary)
      for {
        part, err := partReader.NextPart()
        if err == io.EOF {
          return nntp, nil
        } else if err == nil {
          hdr := part.Header
          // get content type of part
          part_type := hdr.Get("Content-Type")
          // parse content type
          media_type, _, err = mime.ParseMediaType(part_type)
          if err == nil {
            if media_type == "text/plain" {
              att := readAttachmentFromMimePart(part)
              if att != nil {
                nntp.message = att.(nntpAttachment)
                nntp.message.header.Set("Content-Type", part_type)
              }
            } else {
              // non plaintext gets added to attachments
              att := readAttachmentFromMimePart(part)
              if att != nil {
                nntp = nntp.Attach(att).(nntpArticle)
              }
            }
          } else {
            log.Println("part has no content type", err)
          }
          part.Close()
        } else {
          log.Println("failed to load part! ", err)
          return nil, err
        }
      }

    } else if media_type == "message/rfc822" {
      // tripcoded message
      sig := nntp.headers.Get("X-Signature-Ed25519-Sha512", "")
      pk := nntp.Pubkey()
      if pk == "" || sig == "" {
        log.Println("invalid sig or pubkey", sig, pk)
        return nil, errors.New("invalid headers")
      }
      log.Printf("got signed message from %s", pk)
      pk_bytes := unhex(pk)
      sig_bytes := unhex(sig)
      r := bufio.NewReader(msg.Body)
      crlf := []byte{13,10}
      for {
        line, err := r.ReadBytes('\n')
        if err == io.EOF {
          break
        }
        nntp.signedPart.body.Write(line[:len(line)-1])
        nntp.signedPart.body.Write(crlf)
      }
      if nntp.signedPart.body.Len() < 2 {
        log.Println("signed body is too small")
      } else {
        body := nntp.signedPart.body.Bytes()[:nntp.signedPart.body.Len()-2]
        body_hash := sha512.Sum512(body)
        log.Printf("hash=%s", hexify(body_hash[:]))
        if nacl.CryptoVerifyFucky(body_hash[:], sig_bytes, pk_bytes) {
          log.Println("signature is valid :^)")
          return nntp, nil
        } else {
          log.Println("!!!signature is invalid!!!")
        }
      }
    } else {
      // plaintext attachment
      var buff bytes.Buffer
      _, err = io.Copy(&buff, msg.Body)
      nntp.message = createPlaintextAttachment(buff.String())
      return nntp, err
    }
  } else {
    log.Println("failed to read message", err)
    return nil, err
  }
  return nntp, err
}

<|endoftext|>


<commit_before>// Copyright 2016 The Upspin Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package flags defines command-line flags to make them consistent between binaries.
// Not all flags make sense for all binaries.
package flags // import "upspin.io/flags"

import (
	"flag"
	"fmt"
	"path/filepath"
	"strings"

	"upspin.io/config"
	"upspin.io/log"
)

// flagVar represents a flag in this package.
type flagVar struct {
	set  func()        // Set the value at parse time.
	arg  func() string // Return the argument to set the flag.
	arg2 func() string // Return the argument to set the second flag; usually nil.
}

const (
	defaultBlockSize  = 1024 * 1024 // Keep in sync with upspin.BlockSize.]
	defaultHTTPAddr   = ":80"
	defaultHTTPSAddr  = ":443"
	defaultLog        = "info"
	defaultServerKind = "inprocess"
)

// None is the set of no flags. It is rarely needed as most programs
// use either the Server or Client set.
var None = []string{}

// Server is the set of flags most useful in servers. It can be passed as the
// argument to Parse to set up the package for a server.
var Server = []string{
	"config", "log", "http", "https", "letscache", "tls", "addr", "insecure",
}

// Client is the set of flags most useful in clients. It can be passed as the
// argument to Parse to set up the package for a client.
var Client = []string{
	"config", "log", "blocksize", "prudent",
}

// The Parse and Register functions bind these variables to their respective
// command-line flags.
var (
	// BlockSize ("blocksize") is the block size used when writing large files.
	// The default is 1MB.
	BlockSize = defaultBlockSize

	// CacheDir ("cachedir") specifies the directory for the various file
	// caches.
	CacheDir = defaultCacheDir

	defaultCacheDir = filepath.Join(config.Home(), "upspin")

	// Config ("config") names the Upspin configuration file to use.
	Config = defaultConfig

	defaultConfig = filepath.Join(config.Home(), "upspin", "config")

	// HTTPAddr ("http") is the network address on which to listen for
	// incoming insecure network connections.
	HTTPAddr = defaultHTTPAddr

	// HTTPSAddr ("https") is the network address on which to listen for
	// incoming secure network connections.
	HTTPSAddr = defaultHTTPSAddr

	// InsecureHTTP ("insecure") specifies whether to serve insecure HTTP
	// on HTTPAddr, instead of serving HTTPS (secured by TLS) on HTTPSAddr.
	InsecureHTTP = false

	// LetsEncryptCache ("letscache") is the location of a file in which
	// the Let's Encrypt certificates are stored. The containing directory
	// should be owner-accessible only (chmod 0700).
	LetsEncryptCache = ""

	defaultLetsEncryptCache = filepath.Join(config.Home(), "upspin", "letsencrypt")

	// Log ("log") sets the level of logging (implements flag.Value).
	Log logFlag

	// NetAddr ("addr") is the publicly accessible network address of this
	// server.
	NetAddr = ""

	// Project ("project") is the project name on GCP; used by servers,
	// upspin-deploy, and cmd/upspin setupdomain.
	Project = ""

	// ServerConfig ("serverconfig") specifies configuration options for
	// servers in "key=value" pairs.
	ServerConfig []string

	// ServerKind ("kind") is the implementation kind of this server.
	ServerKind = defaultServerKind

	// Prudent ("prudent") sets an extra security mode in the client to
	// check for malicious or buggy servers, at possible cost in
	// performance or convenience. Specifically, one check is that the
	// writer listed in a directory entry is either the owner or a user
	// currently with write permission. This protects against a forged
	// directory entry at the cost of potentially blocking a legitimate
	// file written by a user who no longer has write permission.
	Prudent = false

	// TLSCertFile and TLSKeyFile ("tls") specify the location of a TLS
	// certificate/key pair used for serving TLS (HTTPS).
	TLSCertFile = ""
	TLSKeyFile  = ""
)

// flags is a map of flag registration functions keyed by flag name,
// used by Parse to register specific (or all) flags.
var flags = map[string]*flagVar{
	"addr": strVar(&NetAddr, "addr", NetAddr, "publicly accessible network address (`host:port`)"),
	"blocksize": &flagVar{
		set: func() {
			flag.IntVar(&BlockSize, "blocksize", BlockSize, "`size` of blocks when writing large files")
		},
		arg: func() string {
			if BlockSize == defaultBlockSize {
				return ""
			}
			return fmt.Sprintf("-blocksize=%d", BlockSize)
		},
	},
	"cachedir": strVar(&CacheDir, "cachedir", CacheDir, "`directory` containing all file caches"),
	"config":   strVar(&Config, "config", Config, "user's configuration `file`"),
	"http":     strVar(&HTTPAddr, "http", HTTPAddr, "`address` for incoming insecure network connections"),
	"https":    strVar(&HTTPSAddr, "https", HTTPSAddr, "`address` for incoming secure network connections"),
	"insecure": &flagVar{
		set: func() {
			flag.BoolVar(&InsecureHTTP, "insecure", false, "whether to serve insecure HTTP instead of HTTPS")
		},
		arg: func() string {
			if InsecureHTTP {
				return "-insecure"
			}
			return ""
		},
	},
	"kind":      strVar(&ServerKind, "kind", ServerKind, "server implementation `kind` (inprocess, gcp)"),
	"letscache": strVar(&LetsEncryptCache, "letscache", defaultLetsEncryptCache, "Let's Encrypt cache `directory`"),
	"log": &flagVar{
		set: func() {
			Log.Set("info")
			flag.Var(&Log, "log", "`level` of logging: debug, info, error, disabled")
		},
		arg: func() string { return strArg("log", Log.String(), defaultLog) },
	},
	"project": strVar(&Project, "project", Project, "GCP `project` name"),
	"serverconfig": &flagVar{
		set: func() {
			flag.Var(configFlag{&ServerConfig}, "serverconfig", "comma-separated list of configuration options (key=value) for this server")
		},
		arg: func() string { return strArg("serverconfig", configFlag{&ServerConfig}.String(), "") },
	},
	"prudent": &flagVar{
		set: func() {
			flag.BoolVar(&Prudent, "prudent", false, "protect against malicious directory server")
		},
		arg: func() string {
			if !Prudent {
				return ""
			}
			return "-prudent"
		},
	},
	"tls": &flagVar{
		set: func() {
			flag.StringVar(&TLSCertFile, "tls_cert", "", "TLS Certificate `file` in PEM format")
			flag.StringVar(&TLSKeyFile, "tls_key", "", "TLS Key `file` in PEM format")
		},
		arg:  func() string { return strArg("tls_cert", TLSCertFile, "") },
		arg2: func() string { return strArg("tls_key", TLSKeyFile, "") },
	},
}

// Parse registers the command-line flags for the given default flags list, plus
// any extra flag names, and calls flag.Parse. Passing no flag names in either
// list registers all flags. Passing an unknown name triggers a panic.
// The Server and Client variables contain useful default sets.
//
// Examples:
// 	flags.Parse(flags.Client) // Register all client flags.
//	flags.Parse(flags.Server, "cachedir") // Register all server flags plus cachedir.
// 	flags.Parse(nil) // Register all flags.
// 	flags.Parse(flags.None, "config", "endpoint") // Register only config and endpoint.
func Parse(defaultList []string, extras ...string) {
	if len(defaultList) == 0 && len(extras) == 0 {
		Register()
	} else {
		if len(defaultList) > 0 {
			Register(defaultList...)
		}
		if len(extras) > 0 {
			Register(extras...)
		}
	}
	flag.Parse()
}

// Register registers the command-line flags for the given flag names.
// Unlike Parse, it may be called multiple times.
// Passing zero names install all flags.
// Passing an unknown name triggers a panic.
//
// For example:
// 	flags.Register("config", "endpoint") // Register Config and Endpoint.
// or
// 	flags.Register() // Register all flags.
func Register(names ...string) {
	if len(names) == 0 {
		// Register all flags if no names provided.
		for _, flag := range flags {
			flag.set()
		}
	} else {
		for _, n := range names {
			flag, ok := flags[n]
			if !ok {
				panic(fmt.Sprintf("unknown flag %q", n))
			}
			flag.set()
		}
	}
}

// Args returns a slice of -flag=value strings that will recreate
// the state of the flags. Flags set to their default value are elided.
func Args() []string {
	var args []string
	for _, flag := range flags {
		arg := flag.arg()
		if arg == "" {
			continue
		}
		args = append(args, arg)
		if flag.arg2 != nil {
			args = append(args, flag.arg2())
		}
	}
	return args
}

// strVar returns a flagVar for the given string flag.
func strVar(value *string, name, _default, usage string) *flagVar {
	return &flagVar{
		set: func() {
			flag.StringVar(value, name, _default, usage)
		},
		arg: func() string {
			return strArg(name, *value, _default)
		},
	}
}

// strArg returns a command-line argument that will recreate the flag,
// or the empty string if the value is the default.
func strArg(name, value, _default string) string {
	if value == _default {
		return ""
	}
	return "-" + name + "=" + value
}

type logFlag string

// String implements flag.Value.
func (f logFlag) String() string {
	return string(f)
}

// Set implements flag.Value.
func (f *logFlag) Set(level string) error {
	err := log.SetLevel(level)
	if err != nil {
		return err
	}
	*f = logFlag(log.GetLevel())
	return nil
}

// Get implements flag.Getter.
func (logFlag) Get() interface{} {
	return log.GetLevel()
}

type configFlag struct {
	s *[]string
}

// String implements flag.Value.
func (f configFlag) String() string {
	if f.s == nil {
		return ""
	}
	return strings.Join(*f.s, ",")
}

// Set implements flag.Value.
func (f configFlag) Set(s string) error {
	ss := strings.Split(strings.TrimSpace(s), ",")
	// Drop empty elements.
	for i := 0; i < len(ss); i++ {
		if ss[i] == "" {
			ss = append(ss[:i], ss[i+1:]...)
		}
	}
	*f.s = ss
	return nil
}

// Get implements flag.Getter.
func (f configFlag) Get() interface{} {
	if f.s == nil {
		return ""
	}
	return *f.s
}
<commit_msg>flags: set -letscache default correctly<commit_after>// Copyright 2016 The Upspin Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package flags defines command-line flags to make them consistent between binaries.
// Not all flags make sense for all binaries.
package flags // import "upspin.io/flags"

import (
	"flag"
	"fmt"
	"path/filepath"
	"strings"

	"upspin.io/config"
	"upspin.io/log"
)

// flagVar represents a flag in this package.
type flagVar struct {
	set  func()        // Set the value at parse time.
	arg  func() string // Return the argument to set the flag.
	arg2 func() string // Return the argument to set the second flag; usually nil.
}

const (
	defaultBlockSize  = 1024 * 1024 // Keep in sync with upspin.BlockSize.]
	defaultHTTPAddr   = ":80"
	defaultHTTPSAddr  = ":443"
	defaultLog        = "info"
	defaultServerKind = "inprocess"
)

// None is the set of no flags. It is rarely needed as most programs
// use either the Server or Client set.
var None = []string{}

// Server is the set of flags most useful in servers. It can be passed as the
// argument to Parse to set up the package for a server.
var Server = []string{
	"config", "log", "http", "https", "letscache", "tls", "addr", "insecure",
}

// Client is the set of flags most useful in clients. It can be passed as the
// argument to Parse to set up the package for a client.
var Client = []string{
	"config", "log", "blocksize", "prudent",
}

// The Parse and Register functions bind these variables to their respective
// command-line flags.
var (
	// BlockSize ("blocksize") is the block size used when writing large files.
	// The default is 1MB.
	BlockSize = defaultBlockSize

	// CacheDir ("cachedir") specifies the directory for the various file
	// caches.
	CacheDir = defaultCacheDir

	defaultCacheDir = filepath.Join(config.Home(), "upspin")

	// Config ("config") names the Upspin configuration file to use.
	Config = defaultConfig

	defaultConfig = filepath.Join(config.Home(), "upspin", "config")

	// HTTPAddr ("http") is the network address on which to listen for
	// incoming insecure network connections.
	HTTPAddr = defaultHTTPAddr

	// HTTPSAddr ("https") is the network address on which to listen for
	// incoming secure network connections.
	HTTPSAddr = defaultHTTPSAddr

	// InsecureHTTP ("insecure") specifies whether to serve insecure HTTP
	// on HTTPAddr, instead of serving HTTPS (secured by TLS) on HTTPSAddr.
	InsecureHTTP = false

	// LetsEncryptCache ("letscache") is the location of a file in which
	// the Let's Encrypt certificates are stored. The containing directory
	// should be owner-accessible only (chmod 0700).
	LetsEncryptCache = defaultLetsEncryptCache

	defaultLetsEncryptCache = filepath.Join(config.Home(), "upspin", "letsencrypt")

	// Log ("log") sets the level of logging (implements flag.Value).
	Log logFlag

	// NetAddr ("addr") is the publicly accessible network address of this
	// server.
	NetAddr = ""

	// Project ("project") is the project name on GCP; used by servers,
	// upspin-deploy, and cmd/upspin setupdomain.
	Project = ""

	// ServerConfig ("serverconfig") specifies configuration options for
	// servers in "key=value" pairs.
	ServerConfig []string

	// ServerKind ("kind") is the implementation kind of this server.
	ServerKind = defaultServerKind

	// Prudent ("prudent") sets an extra security mode in the client to
	// check for malicious or buggy servers, at possible cost in
	// performance or convenience. Specifically, one check is that the
	// writer listed in a directory entry is either the owner or a user
	// currently with write permission. This protects against a forged
	// directory entry at the cost of potentially blocking a legitimate
	// file written by a user who no longer has write permission.
	Prudent = false

	// TLSCertFile and TLSKeyFile ("tls") specify the location of a TLS
	// certificate/key pair used for serving TLS (HTTPS).
	TLSCertFile = ""
	TLSKeyFile  = ""
)

// flags is a map of flag registration functions keyed by flag name,
// used by Parse to register specific (or all) flags.
var flags = map[string]*flagVar{
	"addr": strVar(&NetAddr, "addr", NetAddr, "publicly accessible network address (`host:port`)"),
	"blocksize": &flagVar{
		set: func() {
			flag.IntVar(&BlockSize, "blocksize", BlockSize, "`size` of blocks when writing large files")
		},
		arg: func() string {
			if BlockSize == defaultBlockSize {
				return ""
			}
			return fmt.Sprintf("-blocksize=%d", BlockSize)
		},
	},
	"cachedir": strVar(&CacheDir, "cachedir", CacheDir, "`directory` containing all file caches"),
	"config":   strVar(&Config, "config", Config, "user's configuration `file`"),
	"http":     strVar(&HTTPAddr, "http", HTTPAddr, "`address` for incoming insecure network connections"),
	"https":    strVar(&HTTPSAddr, "https", HTTPSAddr, "`address` for incoming secure network connections"),
	"insecure": &flagVar{
		set: func() {
			flag.BoolVar(&InsecureHTTP, "insecure", false, "whether to serve insecure HTTP instead of HTTPS")
		},
		arg: func() string {
			if InsecureHTTP {
				return "-insecure"
			}
			return ""
		},
	},
	"kind":      strVar(&ServerKind, "kind", ServerKind, "server implementation `kind` (inprocess, gcp)"),
	"letscache": strVar(&LetsEncryptCache, "letscache", defaultLetsEncryptCache, "Let's Encrypt cache `directory`"),
	"log": &flagVar{
		set: func() {
			Log.Set("info")
			flag.Var(&Log, "log", "`level` of logging: debug, info, error, disabled")
		},
		arg: func() string { return strArg("log", Log.String(), defaultLog) },
	},
	"project": strVar(&Project, "project", Project, "GCP `project` name"),
	"serverconfig": &flagVar{
		set: func() {
			flag.Var(configFlag{&ServerConfig}, "serverconfig", "comma-separated list of configuration options (key=value) for this server")
		},
		arg: func() string { return strArg("serverconfig", configFlag{&ServerConfig}.String(), "") },
	},
	"prudent": &flagVar{
		set: func() {
			flag.BoolVar(&Prudent, "prudent", false, "protect against malicious directory server")
		},
		arg: func() string {
			if !Prudent {
				return ""
			}
			return "-prudent"
		},
	},
	"tls": &flagVar{
		set: func() {
			flag.StringVar(&TLSCertFile, "tls_cert", "", "TLS Certificate `file` in PEM format")
			flag.StringVar(&TLSKeyFile, "tls_key", "", "TLS Key `file` in PEM format")
		},
		arg:  func() string { return strArg("tls_cert", TLSCertFile, "") },
		arg2: func() string { return strArg("tls_key", TLSKeyFile, "") },
	},
}

// Parse registers the command-line flags for the given default flags list, plus
// any extra flag names, and calls flag.Parse. Passing no flag names in either
// list registers all flags. Passing an unknown name triggers a panic.
// The Server and Client variables contain useful default sets.
//
// Examples:
// 	flags.Parse(flags.Client) // Register all client flags.
//	flags.Parse(flags.Server, "cachedir") // Register all server flags plus cachedir.
// 	flags.Parse(nil) // Register all flags.
// 	flags.Parse(flags.None, "config", "endpoint") // Register only config and endpoint.
func Parse(defaultList []string, extras ...string) {
	if len(defaultList) == 0 && len(extras) == 0 {
		Register()
	} else {
		if len(defaultList) > 0 {
			Register(defaultList...)
		}
		if len(extras) > 0 {
			Register(extras...)
		}
	}
	flag.Parse()
}

// Register registers the command-line flags for the given flag names.
// Unlike Parse, it may be called multiple times.
// Passing zero names install all flags.
// Passing an unknown name triggers a panic.
//
// For example:
// 	flags.Register("config", "endpoint") // Register Config and Endpoint.
// or
// 	flags.Register() // Register all flags.
func Register(names ...string) {
	if len(names) == 0 {
		// Register all flags if no names provided.
		for _, flag := range flags {
			flag.set()
		}
	} else {
		for _, n := range names {
			flag, ok := flags[n]
			if !ok {
				panic(fmt.Sprintf("unknown flag %q", n))
			}
			flag.set()
		}
	}
}

// Args returns a slice of -flag=value strings that will recreate
// the state of the flags. Flags set to their default value are elided.
func Args() []string {
	var args []string
	for _, flag := range flags {
		arg := flag.arg()
		if arg == "" {
			continue
		}
		args = append(args, arg)
		if flag.arg2 != nil {
			args = append(args, flag.arg2())
		}
	}
	return args
}

// strVar returns a flagVar for the given string flag.
func strVar(value *string, name, _default, usage string) *flagVar {
	return &flagVar{
		set: func() {
			flag.StringVar(value, name, _default, usage)
		},
		arg: func() string {
			return strArg(name, *value, _default)
		},
	}
}

// strArg returns a command-line argument that will recreate the flag,
// or the empty string if the value is the default.
func strArg(name, value, _default string) string {
	if value == _default {
		return ""
	}
	return "-" + name + "=" + value
}

type logFlag string

// String implements flag.Value.
func (f logFlag) String() string {
	return string(f)
}

// Set implements flag.Value.
func (f *logFlag) Set(level string) error {
	err := log.SetLevel(level)
	if err != nil {
		return err
	}
	*f = logFlag(log.GetLevel())
	return nil
}

// Get implements flag.Getter.
func (logFlag) Get() interface{} {
	return log.GetLevel()
}

type configFlag struct {
	s *[]string
}

// String implements flag.Value.
func (f configFlag) String() string {
	if f.s == nil {
		return ""
	}
	return strings.Join(*f.s, ",")
}

// Set implements flag.Value.
func (f configFlag) Set(s string) error {
	ss := strings.Split(strings.TrimSpace(s), ",")
	// Drop empty elements.
	for i := 0; i < len(ss); i++ {
		if ss[i] == "" {
			ss = append(ss[:i], ss[i+1:]...)
		}
	}
	*f.s = ss
	return nil
}

// Get implements flag.Getter.
func (f configFlag) Get() interface{} {
	if f.s == nil {
		return ""
	}
	return *f.s
}
<|endoftext|>


<commit_before>package force

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"strings"
)

const (
	grantType    = "password"
	loginUri     = "http://login.salesforce.com@52.36.147.234/services/oauth2/token"
	testLoginUri = "http://test.salesforce.com@52.36.147.234/services/oauth2/token"

	invalidSessionErrorCode = "INVALID_SESSION_ID"
)

type forceOauth struct {
	AccessToken string `json:"access_token"`
	InstanceUrl string `json:"instance_url"`
	Id          string `json:"id"`
	IssuedAt    string `json:"issued_at"`
	Signature   string `json:"signature"`

	clientId      string
	clientSecret  string
	userName      string
	password      string
	securityToken string
	environment   string
}

func (oauth *forceOauth) Validate() error {
	if oauth == nil || len(oauth.InstanceUrl) == 0 || len(oauth.AccessToken) == 0 {
		return fmt.Errorf("Invalid Force Oauth Object: %#v", oauth)
	}

	return nil
}

func (oauth *forceOauth) Expired(apiErrors ApiErrors) bool {
	for _, err := range apiErrors {
		if err.ErrorCode == invalidSessionErrorCode {
			return true
		}
	}

	return false
}

func (oauth *forceOauth) Authenticate() error {
	payload := url.Values{
		"grant_type":    {grantType},
		"client_id":     {oauth.clientId},
		"client_secret": {oauth.clientSecret},
		"username":      {oauth.userName},
		"password":      {fmt.Sprintf("%v%v", oauth.password, oauth.securityToken)},
	}

	// Build Uri
	uri := loginUri
	if oauth.environment == "sandbox" {
		uri = testLoginUri
	}

	// Build Body
	body := strings.NewReader(payload.Encode())

	// Build Request
	req, err := http.NewRequest("POST", uri, body)
	if err != nil {
		return fmt.Errorf("Error creating authenitcation request: %v", err)
	}

	// Add Headers
	req.Header.Set("User-Agent", userAgent)
	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
	req.Header.Set("Accept", responseType)

	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		return fmt.Errorf("Error sending authentication request: %v", err)
	}
	defer resp.Body.Close()

	respBytes, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return fmt.Errorf("Error reading authentication response bytes: %v", err)
	}

	// Attempt to parse response as a force.com api error
	apiError := &ApiError{}
	if err := json.Unmarshal(respBytes, apiError); err == nil {
		// Check if api error is valid
		if apiError.Validate() {
			return apiError
		}
	}

	if err := json.Unmarshal(respBytes, oauth); err != nil {
		return fmt.Errorf("Unable to unmarshal authentication response: %v", err)
	}

	return nil
}
<commit_msg>use DNS so that proxy can run locally<commit_after>package force

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"strings"
)

const (
	grantType    = "password"
	loginUri     = "http://login.salesforce.com@proxy.beomni.com/services/oauth2/token"
	testLoginUri = "http://test.salesforce.com@staging-proxy.beomni.com/services/oauth2/token"

	invalidSessionErrorCode = "INVALID_SESSION_ID"
)

type forceOauth struct {
	AccessToken string `json:"access_token"`
	InstanceUrl string `json:"instance_url"`
	Id          string `json:"id"`
	IssuedAt    string `json:"issued_at"`
	Signature   string `json:"signature"`

	clientId      string
	clientSecret  string
	userName      string
	password      string
	securityToken string
	environment   string
}

func (oauth *forceOauth) Validate() error {
	if oauth == nil || len(oauth.InstanceUrl) == 0 || len(oauth.AccessToken) == 0 {
		return fmt.Errorf("Invalid Force Oauth Object: %#v", oauth)
	}

	return nil
}

func (oauth *forceOauth) Expired(apiErrors ApiErrors) bool {
	for _, err := range apiErrors {
		if err.ErrorCode == invalidSessionErrorCode {
			return true
		}
	}

	return false
}

func (oauth *forceOauth) Authenticate() error {
	payload := url.Values{
		"grant_type":    {grantType},
		"client_id":     {oauth.clientId},
		"client_secret": {oauth.clientSecret},
		"username":      {oauth.userName},
		"password":      {fmt.Sprintf("%v%v", oauth.password, oauth.securityToken)},
	}

	// Build Uri
	uri := loginUri
	if oauth.environment == "sandbox" {
		uri = testLoginUri
	}

	// Build Body
	body := strings.NewReader(payload.Encode())

	// Build Request
	req, err := http.NewRequest("POST", uri, body)
	if err != nil {
		return fmt.Errorf("Error creating authenitcation request: %v", err)
	}

	// Add Headers
	req.Header.Set("User-Agent", userAgent)
	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
	req.Header.Set("Accept", responseType)

	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		return fmt.Errorf("Error sending authentication request: %v", err)
	}
	defer resp.Body.Close()

	respBytes, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return fmt.Errorf("Error reading authentication response bytes: %v", err)
	}

	// Attempt to parse response as a force.com api error
	apiError := &ApiError{}
	if err := json.Unmarshal(respBytes, apiError); err == nil {
		// Check if api error is valid
		if apiError.Validate() {
			return apiError
		}
	}

	if err := json.Unmarshal(respBytes, oauth); err != nil {
		return fmt.Errorf("Unable to unmarshal authentication response: %v", err)
	}

	return nil
}
<|endoftext|>


<commit_before>package cbfsclient

import (
	"encoding/json"
	"fmt"
	"io"
	"math/rand"
	"net/http"
	"net/url"
	"os"
	"strings"
	"time"

	"github.com/dustin/go-saturate"
)

type FetchCallback func(oid string, r io.Reader) error

// Blob info as returned from GetBlobInfos
type BlobInfo struct {
	// Map of node name -> last time the object was validated
	Nodes map[string]time.Time
}

// Find out what nodes contain the given blobs.
func (c Client) GetBlobInfos(oids ...string) (map[string]BlobInfo, error) {
	u := c.URLFor("/.cbfs/blob/info/")
	form := url.Values{"blob": oids}
	res, err := http.PostForm(u, form)
	if err != nil {
		return nil, err
	}
	defer res.Body.Close()
	if res.StatusCode != 200 {
		return nil, fmt.Errorf("HTTP error fetching blob info: %v",
			res.Status)
	}

	d := json.NewDecoder(res.Body)
	rv := map[string]BlobInfo{}
	err = d.Decode(&rv)
	return rv, err
}

type fetchWork struct {
	oid string
	bi  BlobInfo
}

type brokenReader struct{ err error }

func (b brokenReader) Read([]byte) (int, error) {
	return 0, b.err
}

type fetchWorker struct {
	n  StorageNode
	cb FetchCallback
}

func (fw fetchWorker) Work(i interface{}) error {
	oid := i.(string)
	res, err := http.Get(fw.n.BlobURL(oid))
	if err != nil {
		return err
	}
	defer res.Body.Close()
	if res.StatusCode != 200 {
		return fmt.Errorf("HTTP error: %v", res.Status)
	}
	return fw.cb(oid, res.Body)
}

// Fetch many blobs in bulk.
func (c *Client) Blobs(totalConcurrency, destinationConcurrency int,
	cb FetchCallback, oids ...string) error {

	nodeMap, err := c.Nodes()
	if err != nil {
		return err
	}

	dests := make([]string, 0, len(nodeMap))
	for n := range nodeMap {
		dests = append(dests, n)
	}

	infos, err := c.GetBlobInfos(oids...)
	if err != nil {
		return err
	}

	workch := make(chan saturator.WorkInput)
	go func() {
		// Feed the blob (fanout) workers.
		for oid, info := range infos {
			nodes := []string{}
			for n := range info.Nodes {
				nodes = append(nodes, n)
			}
			workch <- saturator.WorkInput{Input: oid, Dests: nodes}
		}

		// Let everything know we're done.
		close(workch)
	}()

	s := saturator.New(dests, func(n string) saturator.Worker {
		return &fetchWorker{nodeMap[n], cb}
	},
		&saturator.Config{
			DestConcurrency:  destinationConcurrency,
			TotalConcurrency: totalConcurrency,
			Retries:          3,
		})

	return s.Saturate(workch)
}

// Grab a file.
//
// This ensures the request is coming directly from a node that
// already has the blob vs. proxying.
func (c Client) Get(path string) (io.ReadCloser, error) {
	req, err := http.NewRequest("GET", c.URLFor(path), nil)
	if err != nil {
		return nil, err
	}
	req.Header.Set("X-CBFS-LocalOnly", "true")

	res, err := http.DefaultClient.Do(req)
	if err != nil {
		return nil, err
	}

	switch res.StatusCode {
	case 200:
		return res.Body, nil
	case 300:
		defer res.Body.Close()
		res, err = http.Get(res.Header.Get("Location"))
		if err != nil {
			return nil, err
		}
		return res.Body, nil
	default:
		defer res.Body.Close()
		return nil, fmt.Errorf("HTTP Error: %v", res.Status)
	}
}

// File info
type FileHandle struct {
	c      Client
	oid    string
	off    int64
	length int64
	meta   FileMeta
	nodes  map[string]time.Time
}

// The nodes containing the files and the last time it was scrubed.
func (f *FileHandle) Nodes() map[string]time.Time {
	return f.nodes
}

// The headers from the file request.
func (f *FileHandle) Meta() FileMeta {
	return f.meta
}

func (f *FileHandle) randomUrl() (string, error) {
	nodes, err := f.c.Nodes()
	if err != nil {
		return "", err
	}

	nodelist := []StorageNode{}
	for k := range nodes {
		if n, ok := nodes[k]; ok {
			nodelist = append(nodelist, n)
		}
	}

	return nodelist[rand.Intn(len(nodelist))].BlobURL(f.oid), nil
}

func (f *FileHandle) Read(b []byte) (int, error) {
	if f.off >= f.length {
		return 0, io.EOF
	}
	n, err := f.ReadAt(b, f.off)
	f.off += int64(n)
	return n, err
}

func (f *FileHandle) Close() error {
	return nil
}

// Implement io.WriterTo
func (f *FileHandle) WriteTo(w io.Writer) (int64, error) {
	u, err := f.randomUrl()
	if err != nil {
		return 0, err
	}
	req, err := http.NewRequest("GET", u, nil)
	if err != nil {
		return 0, err
	}
	if f.off > 0 {
		req.Header.Set("Range",
			fmt.Sprintf("bytes=%v-%v", f.off, f.length-1))
	}

	res, err := http.DefaultClient.Do(req)
	if err != nil {
		return 0, err
	}

	defer res.Body.Close()
	if res.StatusCode != 200 {
		return 0, fmt.Errorf("Unexpected http response: %v", res.Status)
	}

	n, err := io.Copy(w, res.Body)
	f.off += n
	return n, err
}

// Implement io.ReaderAt
func (f *FileHandle) ReadAt(p []byte, off int64) (n int, err error) {
	end := int64(len(p)) + off
	if end >= f.length {
		end = f.length
	}

	u, err := f.randomUrl()
	if err != nil {
		return 0, err
	}

	req, err := http.NewRequest("GET", u, nil)
	if err != nil {
		return 0, err
	}
	req.Header.Set("Range", fmt.Sprintf("bytes=%v-%v", off, end-1))
	res, err := http.DefaultClient.Do(req)
	if err != nil {
		return 0, err
	}
	defer res.Body.Close()
	exp := 206
	if off == 0 && end == f.length {
		exp = 200
	}
	if res.StatusCode != exp {
		return 0, fmt.Errorf("Unexpected http response: %v", res.Status)
	}

	n, err = io.ReadFull(res.Body, p)
	if err == io.ErrUnexpectedEOF {
		err = io.EOF
	}
	return n, err
}

func noSlash(s string) string {
	for strings.HasPrefix(s, "/") {
		s = s[1:]
	}
	return s
}

func (f *FileHandle) Name() string {
	return "" // TODO:  something smarter
}

// Length of this file
func (f *FileHandle) Size() int64 {
	return f.length
}

// file mode (0444)
func (*FileHandle) Mode() os.FileMode {
	return 0444
}

// Last modification time of this file
func (f *FileHandle) ModTime() time.Time {
	return f.meta.Modified
}

// nil
func (*FileHandle) Sys() interface{} {
	return nil
}

// false
func (*FileHandle) IsDir() bool { return false }

// Some assertions around filehandle's applicability
var (
	_ = os.FileInfo(&FileHandle{})
	_ = io.Closer(&FileHandle{})
	_ = io.Reader(&FileHandle{})
	_ = io.ReaderAt(&FileHandle{})
	_ = io.WriterTo(&FileHandle{})
)

// Get a reference to the file at the given path.
func (c Client) OpenFile(path string) (*FileHandle, error) {
	res, err := http.Get(c.URLFor("/.cbfs/info/file/" + noSlash(path)))
	if err != nil {
		return nil, err
	}
	defer res.Body.Close()
	if res.StatusCode != 200 {
		return nil, fmt.Errorf("HTTP error: %v", res.Status)
	}
	j := struct {
		Meta FileMeta
		Path string
	}{}
	d := json.NewDecoder(res.Body)
	err = d.Decode(&j)
	if err != nil {
		return nil, err
	}

	h := j.Meta.OID

	infos, err := c.GetBlobInfos(h)
	if err != nil {
		return nil, err
	}

	return &FileHandle{c, h, 0, j.Meta.Length, j.Meta,
		infos[h].Nodes}, nil
}
<commit_msg>client: satisfy io.Seeker<commit_after>package cbfsclient

import (
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"math/rand"
	"net/http"
	"net/url"
	"os"
	"strings"
	"time"

	"github.com/dustin/go-saturate"
)

type FetchCallback func(oid string, r io.Reader) error

// Blob info as returned from GetBlobInfos
type BlobInfo struct {
	// Map of node name -> last time the object was validated
	Nodes map[string]time.Time
}

// Find out what nodes contain the given blobs.
func (c Client) GetBlobInfos(oids ...string) (map[string]BlobInfo, error) {
	u := c.URLFor("/.cbfs/blob/info/")
	form := url.Values{"blob": oids}
	res, err := http.PostForm(u, form)
	if err != nil {
		return nil, err
	}
	defer res.Body.Close()
	if res.StatusCode != 200 {
		return nil, fmt.Errorf("HTTP error fetching blob info: %v",
			res.Status)
	}

	d := json.NewDecoder(res.Body)
	rv := map[string]BlobInfo{}
	err = d.Decode(&rv)
	return rv, err
}

type fetchWork struct {
	oid string
	bi  BlobInfo
}

type brokenReader struct{ err error }

func (b brokenReader) Read([]byte) (int, error) {
	return 0, b.err
}

type fetchWorker struct {
	n  StorageNode
	cb FetchCallback
}

func (fw fetchWorker) Work(i interface{}) error {
	oid := i.(string)
	res, err := http.Get(fw.n.BlobURL(oid))
	if err != nil {
		return err
	}
	defer res.Body.Close()
	if res.StatusCode != 200 {
		return fmt.Errorf("HTTP error: %v", res.Status)
	}
	return fw.cb(oid, res.Body)
}

// Fetch many blobs in bulk.
func (c *Client) Blobs(totalConcurrency, destinationConcurrency int,
	cb FetchCallback, oids ...string) error {

	nodeMap, err := c.Nodes()
	if err != nil {
		return err
	}

	dests := make([]string, 0, len(nodeMap))
	for n := range nodeMap {
		dests = append(dests, n)
	}

	infos, err := c.GetBlobInfos(oids...)
	if err != nil {
		return err
	}

	workch := make(chan saturator.WorkInput)
	go func() {
		// Feed the blob (fanout) workers.
		for oid, info := range infos {
			nodes := []string{}
			for n := range info.Nodes {
				nodes = append(nodes, n)
			}
			workch <- saturator.WorkInput{Input: oid, Dests: nodes}
		}

		// Let everything know we're done.
		close(workch)
	}()

	s := saturator.New(dests, func(n string) saturator.Worker {
		return &fetchWorker{nodeMap[n], cb}
	},
		&saturator.Config{
			DestConcurrency:  destinationConcurrency,
			TotalConcurrency: totalConcurrency,
			Retries:          3,
		})

	return s.Saturate(workch)
}

// Grab a file.
//
// This ensures the request is coming directly from a node that
// already has the blob vs. proxying.
func (c Client) Get(path string) (io.ReadCloser, error) {
	req, err := http.NewRequest("GET", c.URLFor(path), nil)
	if err != nil {
		return nil, err
	}
	req.Header.Set("X-CBFS-LocalOnly", "true")

	res, err := http.DefaultClient.Do(req)
	if err != nil {
		return nil, err
	}

	switch res.StatusCode {
	case 200:
		return res.Body, nil
	case 300:
		defer res.Body.Close()
		res, err = http.Get(res.Header.Get("Location"))
		if err != nil {
			return nil, err
		}
		return res.Body, nil
	default:
		defer res.Body.Close()
		return nil, fmt.Errorf("HTTP Error: %v", res.Status)
	}
}

// File info
type FileHandle struct {
	c      Client
	oid    string
	off    int64
	length int64
	meta   FileMeta
	nodes  map[string]time.Time
}

// The nodes containing the files and the last time it was scrubed.
func (f *FileHandle) Nodes() map[string]time.Time {
	return f.nodes
}

// The headers from the file request.
func (f *FileHandle) Meta() FileMeta {
	return f.meta
}

func (f *FileHandle) randomUrl() (string, error) {
	nodes, err := f.c.Nodes()
	if err != nil {
		return "", err
	}

	nodelist := []StorageNode{}
	for k := range nodes {
		if n, ok := nodes[k]; ok {
			nodelist = append(nodelist, n)
		}
	}

	return nodelist[rand.Intn(len(nodelist))].BlobURL(f.oid), nil
}

func (f *FileHandle) Read(b []byte) (int, error) {
	if f.off >= f.length {
		return 0, io.EOF
	}
	n, err := f.ReadAt(b, f.off)
	f.off += int64(n)
	return n, err
}

func (f *FileHandle) Close() error {
	return nil
}

// Implement io.WriterTo
func (f *FileHandle) WriteTo(w io.Writer) (int64, error) {
	u, err := f.randomUrl()
	if err != nil {
		return 0, err
	}
	req, err := http.NewRequest("GET", u, nil)
	if err != nil {
		return 0, err
	}
	if f.off > 0 {
		req.Header.Set("Range",
			fmt.Sprintf("bytes=%v-%v", f.off, f.length-1))
	}

	res, err := http.DefaultClient.Do(req)
	if err != nil {
		return 0, err
	}

	defer res.Body.Close()
	if res.StatusCode != 200 {
		return 0, fmt.Errorf("Unexpected http response: %v", res.Status)
	}

	n, err := io.Copy(w, res.Body)
	f.off += n
	return n, err
}

// Implement io.ReaderAt
func (f *FileHandle) ReadAt(p []byte, off int64) (n int, err error) {
	end := int64(len(p)) + off
	if end >= f.length {
		end = f.length
	}

	u, err := f.randomUrl()
	if err != nil {
		return 0, err
	}

	req, err := http.NewRequest("GET", u, nil)
	if err != nil {
		return 0, err
	}
	req.Header.Set("Range", fmt.Sprintf("bytes=%v-%v", off, end-1))
	res, err := http.DefaultClient.Do(req)
	if err != nil {
		return 0, err
	}
	defer res.Body.Close()
	exp := 206
	if off == 0 && end == f.length {
		exp = 200
	}
	if res.StatusCode != exp {
		return 0, fmt.Errorf("Unexpected http response: %v", res.Status)
	}

	n, err = io.ReadFull(res.Body, p)
	if err == io.ErrUnexpectedEOF {
		err = io.EOF
	}
	return n, err
}

func noSlash(s string) string {
	for strings.HasPrefix(s, "/") {
		s = s[1:]
	}
	return s
}

func (f *FileHandle) Name() string {
	return "" // TODO:  something smarter
}

// Length of this file
func (f *FileHandle) Size() int64 {
	return f.length
}

// file mode (0444)
func (*FileHandle) Mode() os.FileMode {
	return 0444
}

// Last modification time of this file
func (f *FileHandle) ModTime() time.Time {
	return f.meta.Modified
}

// nil
func (*FileHandle) Sys() interface{} {
	return nil
}

// false
func (*FileHandle) IsDir() bool { return false }

func (f *FileHandle) Seek(offset int64, whence int) (ret int64, err error) {
	abs := int64(0)
	switch whence {
	case 0:
		abs = offset
	case 1:
		abs = f.off + offset
	case 2:
		abs = f.length + offset
	default:
		return 0, errors.New("bytes: invalid whence")
	}
	if abs < 0 {
		return 0, errors.New("bytes: negative position")
	}
	if abs >= f.length {
		return 0, errors.New("bytes: position out of range")
	}

	f.off = abs
	return f.off, nil
}

// Some assertions around filehandle's applicability
var (
	_ = os.FileInfo(&FileHandle{})
	_ = io.Closer(&FileHandle{})
	_ = io.Reader(&FileHandle{})
	_ = io.ReaderAt(&FileHandle{})
	_ = io.WriterTo(&FileHandle{})
	_ = io.Seeker(&FileHandle{})
)

// Get a reference to the file at the given path.
func (c Client) OpenFile(path string) (*FileHandle, error) {
	res, err := http.Get(c.URLFor("/.cbfs/info/file/" + noSlash(path)))
	if err != nil {
		return nil, err
	}
	defer res.Body.Close()
	if res.StatusCode != 200 {
		return nil, fmt.Errorf("HTTP error: %v", res.Status)
	}
	j := struct {
		Meta FileMeta
		Path string
	}{}
	d := json.NewDecoder(res.Body)
	err = d.Decode(&j)
	if err != nil {
		return nil, err
	}

	h := j.Meta.OID

	infos, err := c.GetBlobInfos(h)
	if err != nil {
		return nil, err
	}

	return &FileHandle{c, h, 0, j.Meta.Length, j.Meta,
		infos[h].Nodes}, nil
}
<|endoftext|>


<commit_before>
package atourofgo

import (
	"math/cmplx"
	"fmt"
)

/**
*	Declares 3 vars:
*
*	- variable1: an integer variable without a value
*	- variable2: a variable without a type initialiser takes the type
*	  of the value assigned to it, in this case a list of strings
*	- variable3: a variable without a type specifier or value
*/
// For some reason this causes an error...
//var variable1 int, variable2, variable3 = "I am a list", "of strings"

/**
*	Example of short assignment. We can use the ':=' operator to assign
*	a local variable inside of a function with an implicity type.
*/
func PrintLocalVariable(){

	localVar := "I am a local variable, short assignment in action."

	fmt.Println(localVar)

}

/**
*	Block variable declaration; variable assignments of different types
*/
var (
	var1 bool = false
	var2 uint32 = 1 << 32 - 1
	var3 complex128 = cmplx.Sqrt(-5 + 12i)
	var4 complex64 = 21i
)

/**
*	Initialised to its 'zero' value, an empty string
*/
var var5 string

/**
*	Example of type convertion, converts an int into a float64 and string
*	respectively
*/
func Convert(integer int) (float64, string) {
	floatValue := float64(integer)
	stringValue := string(integer)
	return floatValue, stringValue
}

/**
* Const declarations
*/
const Thing = "I am a constant"
const (
	Thing2 = "I am another const"
	Thing3 = "I am a const declared in a block"
)

/**
*	Pointers and suchlike...
*/
func PointerExample() {

	value := "a string"
	pointer := &value
	*pointer = "changed the string"
}

/**
*	A simple little struct
*/
type AStruct struct {
	IntegerVar int
	StringVar string
}

/**
*	Exemplifies different struct allocation sytanxes and struct pointer
* 	transparency
*/
func StructExample(){

	struct1 := AStruct{1, "string me"}
	struct2 := AStruct{StringVar: "stringme", IntegerVar: 1}
	pointerToStruct1 := &struct1

	fmt.Printf(string(struct1.IntegerVar))
	fmt.Printf(struct2.StringVar)
	fmt.Printf(pointerToStruct1.StringVar)

}

/**
*	An array of strings
*/
var array [12]string

/**
*	Example function of assigning values at array indeces
*/
func ArrayExample() {
	array[0] = "first string value"
	array[1] = "second string value"
}

/**
*	A slice of integers
*/
var slice []int = []int{1, 2, 3}

/**
*	Hey look ! The zero value for a slice is nil.
*/
var aNilSlice []int = nil;

/**
*	Example function that iterates over a slice
*/
func IterateSlice(sliceIter []string) {
	for i := 0; i < len(sliceIter); i++ {
		fmt.Printf(sliceIter[i])
	}
}

/**
*	Exmaple function that slices a slice in half
*/
func SliceInHalf(sliceMe []int) ([]int, []int) {
	length := len(sliceMe)
	halfWay := length/2
	return sliceMe[0: halfWay], sliceMe[halfWay: length]
}

/**
*	Creates an int slice with 4 0-d values and a capacity of 7
*/
func MakeMeASlice() []int {
	return make([]int, 4, 7)
}

/**
*	Exemplifies appendation
*/
func AddToSlice(appendToMe []int) []int {

	appendToMe = append(appendToMe, 123)
	appendToMe = append(appendToMe, 1, 2, 3)

	return appendToMe
}

/**
*	Use range syntax to loop over the given array several times
*/
func RangeLoopMe(loopMe []string) {
	for i, value := range loopMe {
		fmt.Printf("%d, %q", i, value)
	}
	for i := range loopMe {
		fmt.Printf("%d", i)
	}
	for _, value := range loopMe {
		fmt.Printf("%q", value)
	}
}

/**
*	Map example
*/
var dictionary = map[string] struct {
	Property int
	AnotherProperty string
}{
	"First thing": {
		Property: 1,
		AnotherProperty: "thing",
	},
	"Second thing": {
		Property: 2,
		AnotherProperty: "another thing",
	},
}
<commit_msg>Added func-as-value example<commit_after>
package atourofgo

import (
	"math"
	"math/cmplx"
	"fmt"
)

/**
*	Declares 3 vars:
*
*	- variable1: an integer variable without a value
*	- variable2: a variable without a type initialiser takes the type
*	  of the value assigned to it, in this case a list of strings
*	- variable3: a variable without a type specifier or value
*/
// For some reason this causes an error...
//var variable1 int, variable2, variable3 = "I am a list", "of strings"

/**
*	Example of short assignment. We can use the ':=' operator to assign
*	a local variable inside of a function with an implicity type.
*/
func PrintLocalVariable(){

	localVar := "I am a local variable, short assignment in action."

	fmt.Println(localVar)

}

/**
*	Block variable declaration; variable assignments of different types
*/
var (
	var1 bool = false
	var2 uint32 = 1 << 32 - 1
	var3 complex128 = cmplx.Sqrt(-5 + 12i)
	var4 complex64 = 21i
)

/**
*	Initialised to its 'zero' value, an empty string
*/
var var5 string

/**
*	Example of type convertion, converts an int into a float64 and string
*	respectively
*/
func Convert(integer int) (float64, string) {
	floatValue := float64(integer)
	stringValue := string(integer)
	return floatValue, stringValue
}

/**
* Const declarations
*/
const Thing = "I am a constant"
const (
	Thing2 = "I am another const"
	Thing3 = "I am a const declared in a block"
)

/**
*	Pointers and suchlike...
*/
func PointerExample() {

	value := "a string"
	pointer := &value
	*pointer = "changed the string"
}

/**
*	A simple little struct
*/
type AStruct struct {
	IntegerVar int
	StringVar string
}

/**
*	Exemplifies different struct allocation sytanxes and struct pointer
* 	transparency
*/
func StructExample(){

	struct1 := AStruct{1, "string me"}
	struct2 := AStruct{StringVar: "stringme", IntegerVar: 1}
	pointerToStruct1 := &struct1

	fmt.Printf(string(struct1.IntegerVar))
	fmt.Printf(struct2.StringVar)
	fmt.Printf(pointerToStruct1.StringVar)

}

/**
*	An array of strings
*/
var array [12]string

/**
*	Example function of assigning values at array indeces
*/
func ArrayExample() {
	array[0] = "first string value"
	array[1] = "second string value"
}

/**
*	A slice of integers
*/
var slice []int = []int{1, 2, 3}

/**
*	Hey look ! The zero value for a slice is nil.
*/
var aNilSlice []int = nil;

/**
*	Example function that iterates over a slice
*/
func IterateSlice(sliceIter []string) {
	for i := 0; i < len(sliceIter); i++ {
		fmt.Printf(sliceIter[i])
	}
}

/**
*	Exmaple function that slices a slice in half
*/
func SliceInHalf(sliceMe []int) ([]int, []int) {
	length := len(sliceMe)
	halfWay := length/2
	return sliceMe[0: halfWay], sliceMe[halfWay: length]
}

/**
*	Creates an int slice with 4 0-d values and a capacity of 7
*/
func MakeMeASlice() []int {
	return make([]int, 4, 7)
}

/**
*	Exemplifies appendation
*/
func AddToSlice(appendToMe []int) []int {

	appendToMe = append(appendToMe, 123)
	appendToMe = append(appendToMe, 1, 2, 3)

	return appendToMe
}

/**
*	Use range syntax to loop over the given array several times
*/
func RangeLoopMe(loopMe []string) {
	for i, value := range loopMe {
		fmt.Printf("%d, %q", i, value)
	}
	for i := range loopMe {
		fmt.Printf("%d", i)
	}
	for _, value := range loopMe {
		fmt.Printf("%q", value)
	}
}

/**
*	Map declaration example
*/
var dictionary = map[string] struct {
	Property int
	AnotherProperty string
}{
	"First thing": {
		Property: 1,
		AnotherProperty: "thing",
	},
	"Second thing": {
		Property: 2,
		AnotherProperty: "another thing",
	},
}

/**
*	Example of mutating our dictionary
*/
func MutateMyDictionary() {

	muteDic := map[string] int {"One": 1, "Two": 2}
	muteDic["Three"] = 4

	delete(muteDic, "Three")

	if one, ok := muteDic["One"]; ok {
		fmt.Printf("The first thing: %d", one)
	}
}

/**
*	Example of func values
*/
func FuncValueExample() func(x float64) float64{
	hypot := func(x, y float64) float64 {
		return math.Sqrt(x*x + y*y)
	}
	closure := func(x float64) float64{
		return hypot(x, 5)
	}
	return closure
}
<|endoftext|>


<commit_before>package server

import (
	"github.com/concourse/atc/auth/verifier"
	"github.com/dghubble/oauth1"
	"golang.org/x/net/context"
	"golang.org/x/oauth2"
	"net/http"
	"time"
)

type Provider struct {
	*oauth1.Config
	verifier.Verifier
	secrets map[string]string
}

func (p *Provider) AuthCodeURL(state string, opts ...oauth2.AuthCodeOption) string {
	requestToken, requestSecret, err := p.Config.RequestToken()
	if err != nil {
		panic(err)
	}
	authorizationURL, err := p.Config.AuthorizationURL(requestToken)
	if err != nil {
		panic(err)
	}
	p.secrets[requestToken] = requestSecret
	return authorizationURL.String()
}

func (p *Provider) Client(ctx context.Context, t *oauth2.Token) *http.Client {
	return p.Config.Client(ctx, &oauth1.Token{Token: t.AccessToken, TokenSecret: t.RefreshToken})
}

func (p *Provider) Exchange(ctx context.Context, req *http.Request) (*oauth2.Token, error) {
	requestToken, verifier, err := oauth1.ParseAuthorizationCallback(req)
	if err != nil {
		panic(err)
	}

	requestSecret := p.secrets[requestToken]

	accessToken, accessSecret, err := p.Config.AccessToken(requestToken, requestSecret, verifier)
	if err != nil {
		panic(err)
	}

	token := oauth1.NewToken(accessToken, accessSecret)

	return &oauth2.Token{
		AccessToken:  token.Token,
		TokenType:    "Bearer",
		RefreshToken: token.TokenSecret,
		Expiry:       time.Time{},
	}, nil
}

func (*Provider) PreTokenClient() (*http.Client, error) {
	return &http.Client{
		Transport: &http.Transport{
			Proxy:             http.ProxyFromEnvironment,
			DisableKeepAlives: true,
		},
	}, nil
}
<commit_msg>Don't panic in case the OAuth 1 "Exchange" goes wrong<commit_after>package server

import (
	"github.com/concourse/atc/auth/verifier"
	"github.com/dghubble/oauth1"
	"golang.org/x/net/context"
	"golang.org/x/oauth2"
	"net/http"
	"time"
)

type Provider struct {
	*oauth1.Config
	verifier.Verifier
	secrets map[string]string
}

func (p *Provider) AuthCodeURL(state string, opts ...oauth2.AuthCodeOption) string {
	requestToken, requestSecret, err := p.Config.RequestToken()
	if err != nil {
		panic(err)
	}
	authorizationURL, err := p.Config.AuthorizationURL(requestToken)
	if err != nil {
		panic(err)
	}
	p.secrets[requestToken] = requestSecret
	return authorizationURL.String()
}

func (p *Provider) Client(ctx context.Context, t *oauth2.Token) *http.Client {
	return p.Config.Client(ctx, &oauth1.Token{Token: t.AccessToken, TokenSecret: t.RefreshToken})
}

func (p *Provider) Exchange(ctx context.Context, req *http.Request) (*oauth2.Token, error) {
	requestToken, verifier, err := oauth1.ParseAuthorizationCallback(req)
	if err != nil {
		return nil, err
	}

	requestSecret := p.secrets[requestToken]

	accessToken, accessSecret, err := p.Config.AccessToken(requestToken, requestSecret, verifier)
	if err != nil {
		return nil, err
	}

	token := oauth1.NewToken(accessToken, accessSecret)

	return &oauth2.Token{
		AccessToken:  token.Token,
		TokenType:    "Bearer",
		RefreshToken: token.TokenSecret,
		Expiry:       time.Time{},
	}, nil
}

func (*Provider) PreTokenClient() (*http.Client, error) {
	return &http.Client{
		Transport: &http.Transport{
			Proxy:             http.ProxyFromEnvironment,
			DisableKeepAlives: true,
		},
	}, nil
}
<|endoftext|>


<commit_before>// Copyright 2013 Google. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file or at
// https://developers.google.com/open-source/licenses/bsd

package github

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/http/httptest"
	"net/url"
	"reflect"
	"strings"
	"testing"
)

var (
	// mux is the HTTP request multiplexer used with the test server.
	mux *http.ServeMux

	// client is the GitHub client being tested.
	client *Client

	// server is a test HTTP server used to provide mock API responses.
	server *httptest.Server
)

// setup sets up a test HTTP server along with a github.Client that is
// configured to talk to that test server.  Tests should register handlers on
// mux which provide mock responses for the API method being tested.
func setup() {
	// test server
	mux = http.NewServeMux()
	server = httptest.NewServer(mux)

	// github client configured to use test server
	client = NewClient(nil)
	client.BaseURL, _ = url.Parse(server.URL)
}

// teardown closes the test HTTP server.
func teardown() {
	server.Close()
}

func TestNewClient(t *testing.T) {
	c := NewClient(nil)

	if c.BaseURL.String() != defaultBaseURL {
		t.Errorf("NewClient BaseURL = %v, want %v", c.BaseURL.String(), defaultBaseURL)
	}
	if c.UserAgent != userAgent {
		t.Errorf("NewClient UserAgent = %v, want %v", c.UserAgent, userAgent)
	}
}

func TestNewRequest(t *testing.T) {
	c := NewClient(nil)

	inURL, outURL := "/foo", defaultBaseURL+"foo"
	inBody, outBody := &User{Login: "l"}, `{"login":"l"}`+"\n"
	req, _ := c.NewRequest("GET", inURL, inBody)

	// test that relative URL was expanded
	if req.URL.String() != outURL {
		t.Errorf("NewRequest(%v) URL = %v, want %v", inURL, req.URL, outURL)
	}

	// test that body was JSON encoded
	body, _ := ioutil.ReadAll(req.Body)
	if string(body) != outBody {
		t.Errorf("NewRequest(%v) Body = %v, want %v", inBody, string(body), outBody)
	}

	// test that default user-agent is attached to the request
	userAgent := req.Header.Get("User-Agent")
	if c.UserAgent != userAgent {
		t.Errorf("NewRequest() User-Agent = %v, want %v", userAgent, c.UserAgent)
	}
}

func TestNewRequest_invalidJSON(t *testing.T) {
	c := NewClient(nil)

	type T struct {
		A map[int]interface{}
	}
	_, err := c.NewRequest("GET", "/", &T{})

	if err == nil {
		t.Error("Expected error to be returned.")
	}
	if err, ok := err.(*json.UnsupportedTypeError); !ok {
		t.Errorf("Expected a JSON error; got %#v.", err)
	}
}

func TestNewRequest_badURL(t *testing.T) {
	c := NewClient(nil)
	_, err := c.NewRequest("GET", ":", nil)
	if err == nil {
		t.Error("Expected error to be returned.")
	}
	if err, ok := err.(*url.Error); !ok || err.Op != "parse" {
		t.Errorf("Expected a URL parsing error; got %+v.", err)
	}
}

func TestDo(t *testing.T) {
	setup()
	defer teardown()

	type foo struct {
		A string
	}

	mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		if m := "GET"; m != r.Method {
			t.Errorf("Request method = %v, want %v", r.Method, m)
		}
		fmt.Fprint(w, `{"A":"a"}`)
	})

	req, _ := client.NewRequest("GET", "/", nil)
	body := new(foo)
	client.Do(req, body)

	want := &foo{"a"}
	if !reflect.DeepEqual(body, want) {
		t.Errorf("Response body = %v, want %v", body, want)
	}
}

func TestDo_httpError(t *testing.T) {
	setup()
	defer teardown()

	mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		http.Error(w, "Bad Request", 400)
	})

	req, _ := client.NewRequest("GET", "/", nil)
	_, err := client.Do(req, nil)

	if err == nil {
		t.Error("Expected HTTP 400 error.")
	}
}

// Test handling of an error caused by the internal http client's Do()
// function.  A redirect loop is pretty unlikely to occur within the GitHub
// API, but does allow us to exercise the right code path.
func TestDo_redirectLoop(t *testing.T) {
	setup()
	defer teardown()

	mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		http.Redirect(w, r, "/", http.StatusFound)
	})

	req, _ := client.NewRequest("GET", "/", nil)
	_, err := client.Do(req, nil)

	if err == nil {
		t.Error("Expected error to be returned.")
	}
	if err, ok := err.(*url.Error); !ok {
		t.Errorf("Expected a URL error; got %#v.", err)
	}
}

func TestCheckResponse(t *testing.T) {
	res := &http.Response{
		Request:    &http.Request{},
		StatusCode: http.StatusBadRequest,
		Body: ioutil.NopCloser(strings.NewReader(`{"message":"m",
			"errors": [{"resource": "r", "field": "f", "code": "c"}]}`)),
	}
	err := CheckResponse(res).(*ErrorResponse)

	if err == nil {
		t.Errorf("Expected error response.")
	}

	want := &ErrorResponse{
		Response: res,
		Message:  "m",
		Errors:   []Error{Error{Resource: "r", Field: "f", Code: "c"}},
	}
	if !reflect.DeepEqual(err, want) {
		t.Errorf("Error = %#v, want %#v", err, want)
	}
}

// ensure that we properly handle API errors that do not contain a response
// body
func TestCheckResponse_noBody(t *testing.T) {
	res := &http.Response{
		Request:    &http.Request{},
		StatusCode: http.StatusBadRequest,
		Body:       ioutil.NopCloser(strings.NewReader("")),
	}
	err := CheckResponse(res).(*ErrorResponse)

	if err == nil {
		t.Errorf("Expected error response.")
	}

	want := &ErrorResponse{
		Response: res,
	}
	if !reflect.DeepEqual(err, want) {
		t.Errorf("Error = %#v, want %#v", err, want)
	}
}

func TestParseBooleanResponse_true(t *testing.T) {
	result, err := parseBoolResponse(nil)

	if err != nil {
		t.Errorf("parseBoolResponse returned error: %+v", err)
	}

	if want := true; result != want {
		t.Errorf("parseBoolResponse returned %+v, want: %+v", result, want)
	}
}

func TestParseBooleanResponse_false(t *testing.T) {
	v := &ErrorResponse{Response: &http.Response{StatusCode: http.StatusNotFound}}
	result, err := parseBoolResponse(v)

	if err != nil {
		t.Errorf("parseBoolResponse returned error: %+v", err)
	}

	if want := false; result != want {
		t.Errorf("parseBoolResponse returned %+v, want: %+v", result, want)
	}
}

func TestParseBooleanResponse_error(t *testing.T) {
	v := &ErrorResponse{Response: &http.Response{StatusCode: http.StatusBadRequest}}
	result, err := parseBoolResponse(v)

	if err == nil {
		t.Errorf("Expected error to be returned.")
	}

	if want := false; result != want {
		t.Errorf("parseBoolResponse returned %+v, want: %+v", result, want)
	}
}

func TestErrorResponse_Error(t *testing.T) {
	res := &http.Response{Request: &http.Request{}}
	err := ErrorResponse{Message: "m", Response: res}
	if err.Error() == "" {
		t.Errorf("Expected non-empty ErrorResponse.Error()")
	}
}

func TestError_Error(t *testing.T) {
	err := Error{}
	if err.Error() == "" {
		t.Errorf("Expected non-empty Error.Error()")
	}
}

func TestRateLimit(t *testing.T) {
	setup()
	defer teardown()

	mux.HandleFunc("/rate_limit", func(w http.ResponseWriter, r *http.Request) {
		if m := "GET"; m != r.Method {
			t.Errorf("Request method = %v, want %v", r.Method, m)
		}
		fmt.Fprint(w, `{"rate":{"limit":2,"remaining":1}}`)
	})

	rate, err := client.RateLimit()
	if err != nil {
		t.Errorf("Rate limit returned error: %v", err)
	}

	want := &Rate{Limit: 2, Remaining: 1}
	if !reflect.DeepEqual(rate, want) {
		t.Errorf("RateLimit returned %+v, want %+v", rate, want)
	}
}

func TestUnauthenticatedRateLimitedTransport(t *testing.T) {
	setup()
	defer teardown()

	mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		var v, want string
		q := r.URL.Query()
		if v, want = q.Get("client_id"), "id"; v != want {
			t.Errorf("OAuth Client ID = %v, want %v", v, want)
		}
		if v, want = q.Get("client_secret"), "secret"; v != want {
			t.Errorf("OAuth Client Secret = %v, want %v", v, want)
		}
	})

	tp := &UnauthenticatedRateLimitedTransport{
		ClientID:     "id",
		ClientSecret: "secret",
	}
	unauthedClient := NewClient(tp.Client())
	unauthedClient.BaseURL = client.BaseURL
	req, _ := unauthedClient.NewRequest("GET", "/", nil)
	unauthedClient.Do(req, nil)
}
<commit_msg>more UnauthenticatedRateLimitedTransport tests<commit_after>// Copyright 2013 Google. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file or at
// https://developers.google.com/open-source/licenses/bsd

package github

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/http/httptest"
	"net/url"
	"reflect"
	"strings"
	"testing"
)

var (
	// mux is the HTTP request multiplexer used with the test server.
	mux *http.ServeMux

	// client is the GitHub client being tested.
	client *Client

	// server is a test HTTP server used to provide mock API responses.
	server *httptest.Server
)

// setup sets up a test HTTP server along with a github.Client that is
// configured to talk to that test server.  Tests should register handlers on
// mux which provide mock responses for the API method being tested.
func setup() {
	// test server
	mux = http.NewServeMux()
	server = httptest.NewServer(mux)

	// github client configured to use test server
	client = NewClient(nil)
	client.BaseURL, _ = url.Parse(server.URL)
}

// teardown closes the test HTTP server.
func teardown() {
	server.Close()
}

func TestNewClient(t *testing.T) {
	c := NewClient(nil)

	if c.BaseURL.String() != defaultBaseURL {
		t.Errorf("NewClient BaseURL = %v, want %v", c.BaseURL.String(), defaultBaseURL)
	}
	if c.UserAgent != userAgent {
		t.Errorf("NewClient UserAgent = %v, want %v", c.UserAgent, userAgent)
	}
}

func TestNewRequest(t *testing.T) {
	c := NewClient(nil)

	inURL, outURL := "/foo", defaultBaseURL+"foo"
	inBody, outBody := &User{Login: "l"}, `{"login":"l"}`+"\n"
	req, _ := c.NewRequest("GET", inURL, inBody)

	// test that relative URL was expanded
	if req.URL.String() != outURL {
		t.Errorf("NewRequest(%v) URL = %v, want %v", inURL, req.URL, outURL)
	}

	// test that body was JSON encoded
	body, _ := ioutil.ReadAll(req.Body)
	if string(body) != outBody {
		t.Errorf("NewRequest(%v) Body = %v, want %v", inBody, string(body), outBody)
	}

	// test that default user-agent is attached to the request
	userAgent := req.Header.Get("User-Agent")
	if c.UserAgent != userAgent {
		t.Errorf("NewRequest() User-Agent = %v, want %v", userAgent, c.UserAgent)
	}
}

func TestNewRequest_invalidJSON(t *testing.T) {
	c := NewClient(nil)

	type T struct {
		A map[int]interface{}
	}
	_, err := c.NewRequest("GET", "/", &T{})

	if err == nil {
		t.Error("Expected error to be returned.")
	}
	if err, ok := err.(*json.UnsupportedTypeError); !ok {
		t.Errorf("Expected a JSON error; got %#v.", err)
	}
}

func TestNewRequest_badURL(t *testing.T) {
	c := NewClient(nil)
	_, err := c.NewRequest("GET", ":", nil)
	if err == nil {
		t.Error("Expected error to be returned.")
	}
	if err, ok := err.(*url.Error); !ok || err.Op != "parse" {
		t.Errorf("Expected a URL parsing error; got %+v.", err)
	}
}

func TestDo(t *testing.T) {
	setup()
	defer teardown()

	type foo struct {
		A string
	}

	mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		if m := "GET"; m != r.Method {
			t.Errorf("Request method = %v, want %v", r.Method, m)
		}
		fmt.Fprint(w, `{"A":"a"}`)
	})

	req, _ := client.NewRequest("GET", "/", nil)
	body := new(foo)
	client.Do(req, body)

	want := &foo{"a"}
	if !reflect.DeepEqual(body, want) {
		t.Errorf("Response body = %v, want %v", body, want)
	}
}

func TestDo_httpError(t *testing.T) {
	setup()
	defer teardown()

	mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		http.Error(w, "Bad Request", 400)
	})

	req, _ := client.NewRequest("GET", "/", nil)
	_, err := client.Do(req, nil)

	if err == nil {
		t.Error("Expected HTTP 400 error.")
	}
}

// Test handling of an error caused by the internal http client's Do()
// function.  A redirect loop is pretty unlikely to occur within the GitHub
// API, but does allow us to exercise the right code path.
func TestDo_redirectLoop(t *testing.T) {
	setup()
	defer teardown()

	mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		http.Redirect(w, r, "/", http.StatusFound)
	})

	req, _ := client.NewRequest("GET", "/", nil)
	_, err := client.Do(req, nil)

	if err == nil {
		t.Error("Expected error to be returned.")
	}
	if err, ok := err.(*url.Error); !ok {
		t.Errorf("Expected a URL error; got %#v.", err)
	}
}

func TestCheckResponse(t *testing.T) {
	res := &http.Response{
		Request:    &http.Request{},
		StatusCode: http.StatusBadRequest,
		Body: ioutil.NopCloser(strings.NewReader(`{"message":"m",
			"errors": [{"resource": "r", "field": "f", "code": "c"}]}`)),
	}
	err := CheckResponse(res).(*ErrorResponse)

	if err == nil {
		t.Errorf("Expected error response.")
	}

	want := &ErrorResponse{
		Response: res,
		Message:  "m",
		Errors:   []Error{Error{Resource: "r", Field: "f", Code: "c"}},
	}
	if !reflect.DeepEqual(err, want) {
		t.Errorf("Error = %#v, want %#v", err, want)
	}
}

// ensure that we properly handle API errors that do not contain a response
// body
func TestCheckResponse_noBody(t *testing.T) {
	res := &http.Response{
		Request:    &http.Request{},
		StatusCode: http.StatusBadRequest,
		Body:       ioutil.NopCloser(strings.NewReader("")),
	}
	err := CheckResponse(res).(*ErrorResponse)

	if err == nil {
		t.Errorf("Expected error response.")
	}

	want := &ErrorResponse{
		Response: res,
	}
	if !reflect.DeepEqual(err, want) {
		t.Errorf("Error = %#v, want %#v", err, want)
	}
}

func TestParseBooleanResponse_true(t *testing.T) {
	result, err := parseBoolResponse(nil)

	if err != nil {
		t.Errorf("parseBoolResponse returned error: %+v", err)
	}

	if want := true; result != want {
		t.Errorf("parseBoolResponse returned %+v, want: %+v", result, want)
	}
}

func TestParseBooleanResponse_false(t *testing.T) {
	v := &ErrorResponse{Response: &http.Response{StatusCode: http.StatusNotFound}}
	result, err := parseBoolResponse(v)

	if err != nil {
		t.Errorf("parseBoolResponse returned error: %+v", err)
	}

	if want := false; result != want {
		t.Errorf("parseBoolResponse returned %+v, want: %+v", result, want)
	}
}

func TestParseBooleanResponse_error(t *testing.T) {
	v := &ErrorResponse{Response: &http.Response{StatusCode: http.StatusBadRequest}}
	result, err := parseBoolResponse(v)

	if err == nil {
		t.Errorf("Expected error to be returned.")
	}

	if want := false; result != want {
		t.Errorf("parseBoolResponse returned %+v, want: %+v", result, want)
	}
}

func TestErrorResponse_Error(t *testing.T) {
	res := &http.Response{Request: &http.Request{}}
	err := ErrorResponse{Message: "m", Response: res}
	if err.Error() == "" {
		t.Errorf("Expected non-empty ErrorResponse.Error()")
	}
}

func TestError_Error(t *testing.T) {
	err := Error{}
	if err.Error() == "" {
		t.Errorf("Expected non-empty Error.Error()")
	}
}

func TestRateLimit(t *testing.T) {
	setup()
	defer teardown()

	mux.HandleFunc("/rate_limit", func(w http.ResponseWriter, r *http.Request) {
		if m := "GET"; m != r.Method {
			t.Errorf("Request method = %v, want %v", r.Method, m)
		}
		fmt.Fprint(w, `{"rate":{"limit":2,"remaining":1}}`)
	})

	rate, err := client.RateLimit()
	if err != nil {
		t.Errorf("Rate limit returned error: %v", err)
	}

	want := &Rate{Limit: 2, Remaining: 1}
	if !reflect.DeepEqual(rate, want) {
		t.Errorf("RateLimit returned %+v, want %+v", rate, want)
	}
}

func TestUnauthenticatedRateLimitedTransport(t *testing.T) {
	setup()
	defer teardown()

	mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		var v, want string
		q := r.URL.Query()
		if v, want = q.Get("client_id"), "id"; v != want {
			t.Errorf("OAuth Client ID = %v, want %v", v, want)
		}
		if v, want = q.Get("client_secret"), "secret"; v != want {
			t.Errorf("OAuth Client Secret = %v, want %v", v, want)
		}
	})

	tp := &UnauthenticatedRateLimitedTransport{
		ClientID:     "id",
		ClientSecret: "secret",
	}
	unauthedClient := NewClient(tp.Client())
	unauthedClient.BaseURL = client.BaseURL
	req, _ := unauthedClient.NewRequest("GET", "/", nil)
	unauthedClient.Do(req, nil)
}

func TestUnauthenticatedRateLimitedTransport_missingFields(t *testing.T) {
	// missing ClientID
	tp := &UnauthenticatedRateLimitedTransport{
		ClientSecret: "secret",
	}
	_, err := tp.RoundTrip(nil)
	if err == nil {
		t.Errorf("Expected error to be returned")
	}

	// missing ClientSecret
	tp = &UnauthenticatedRateLimitedTransport{
		ClientID: "id",
	}
	_, err = tp.RoundTrip(nil)
	if err == nil {
		t.Errorf("Expected error to be returned")
	}
}

func TestUnauthenticatedRateLimitedTransport_transport(t *testing.T) {
	// default transport
	tp := &UnauthenticatedRateLimitedTransport{
		ClientID:     "id",
		ClientSecret: "secret",
	}
	if tp.transport() != http.DefaultTransport {
		t.Errorf("Expected http.DefaultTransport to be used.")
	}

	// custom transport
	tp = &UnauthenticatedRateLimitedTransport{
		ClientID:     "id",
		ClientSecret: "secret",
		Transport: &http.Transport{},
	}
	if tp.transport() == http.DefaultTransport {
		t.Errorf("Expected custom transport to be used.")
	}
}
<|endoftext|>