Giter VIP home page Giter VIP logo

multipart-post's Introduction

Socketry

Gem Version Build Status Code Climate Coverage Status MIT licensed

High-level Ruby socket library with support for TCP, UDP, and SSL sockets.

Implements thread-safe timeouts using asynchronous I/O and high-precision monotonic timers.

Motivation

By default, Ruby sockets do not provide a built-in timeout mechanism. The only timeout mechanism provided by the language leverages timeout.rb, which uses unsafe multithreaded behaviors to implement timeouts.

While Socketry provides a synchronous, blocking API similar to Ruby's own TCPSocket and UDPSocket classes, behind the scenes it uses non-blocking I/O to implement thread-safe timeouts.

Installation

Add this line to your application's Gemfile:

gem "socketry"

And then execute:

$ bundle

Or install it yourself as:

$ gem install socketry

Basic Usage

Below is a basic example of how to use Socketry to make an HTTPS request:

require "socketry"

socket = Socketry::SSL::Socket.connect("github.com", 443)
socket.writepartial("GET / HTTP/1.0\r\nHost: github.com\r\n\r\n")
p socket.readpartial(1024)

TCP, SSL, and UDP servers and sockets also available.

Documentation

Please see the Socketry wiki for more detailed documentation and usage notes.

YARD API documentation is also available.

Supported Ruby Versions

This library aims to support and is tested against the following Ruby versions:

  • Ruby 2.2.6+
  • Ruby 2.3
  • Ruby 2.4
  • Ruby 2.5
  • JRuby 9.1.6.0+

If something doesn't work on one of these versions, it's a bug.

This library may inadvertently work (or seem to work) on other Ruby versions, however support will only be provided for the versions listed above.

If you would like this library to support another Ruby version or implementation, you may volunteer to be a maintainer. Being a maintainer entails making sure all tests run and pass on that implementation. When something breaks on your implementation, you will be responsible for providing patches in a timely fashion. If critical issues for a particular implementation exist at the time of a major release, support for that Ruby version may be dropped.

Contributing

  • Fork this repository on github
  • Make your changes and send us a pull request
  • If we like them we'll merge them
  • If we've accepted a patch, feel free to ask for commit access

License

Copyright (c) 2016 Tony Arcieri. Distributed under the MIT License. See LICENSE.txt for further details.

multipart-post's People

Contributors

arsduo avatar captainbeardo avatar christineyen avatar ehutzelman avatar ethnt avatar feuda avatar gernberg avatar hasimo avatar ioquatix avatar jagtesh avatar janpio avatar jasonyork avatar jmhodges avatar jordimassaguerpla avatar jspanjers avatar jwagener avatar lcpriest avatar leocassarani avatar lewiscowles1986 avatar lonre avatar lukeredpath avatar m-nakamura145 avatar mcolyer avatar mislav avatar mlooney avatar nicksieger avatar olleolleolle avatar patrickdavey avatar petergoldstein avatar steved avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

multipart-post's Issues

About the end of the line have two CRLFs

0af8dde#diff-9d2b9709d7d6d6706a5d7de795405083R62

Current version of multipart-post have two \r\n in the end of line:

class EpiloguePart
  include Part
  def initialize(boundary)
    @part = "--#{boundary}--\r\n\r\n"
    @io = StringIO.new(@part)
  end
end

But the same case in other programming languages stdlib there just have one
For example Go:

http://golang.org/src/mime/multipart/writer.go?s=619:653#L160

153 func (w *Writer) Close() error {
154     if w.lastpart != nil {
155         if err := w.lastpart.close(); err != nil {
156             return err
157         }
158         w.lastpart = nil
159     }
160     _, err := fmt.Fprintf(w.w, "\r\n--%s--\r\n", w.boundary)
161     return err
162 }

Is \r\n\r\n in the end of the line is correct?
This will not let our project upload success, becuase upload server care about that.

2.2.2 breaks compatibility with older Rubies

2.2.2 dropped support for Ruby <2.6.0. This is a backwards-incompatible change, so per semver it should be a major version bump. Older Rubies (with older rubygems) depending on multipart-post ~> 2.x will now fail to install:

$ docker run --rm -it ruby:2.5-alpine gem install multipart-post -v '~> 2.2'
Fetching multipart-post-2.2.2.gem
ERROR:  Error installing multipart-post:
	The last version of multipart-post (~> 2.2) to support your Ruby & RubyGems was 2.2.0. Try installing it with `gem install multipart-post -v 2.2.0`
	multipart-post requires Ruby version >= 2.6.0. The current ruby version is 2.5.9.229.

I'd suggest that the current gem should be rereleased as 3.0, and 2.2.2 be yanked.

I recognize that 2.5 (and 2.6) are old Rubies at this point, so I have no overall complaint about dropping support in a newer gem version here. It's just that the way this was implemented, those older Rubies can't even keep going with version constraints that were previously valid.

Thanks!

Feature request: Don't set "local.path" as a default filename to prevent it from being detected as a binary

Thank you for useful gem!

On some systems, a preview of an image is not displayed correctly when I specify "local.path" as a filename. To solve this behavior, could you simply set "file" as a default filename?

If you agree with this issue, I will create a pull request. I would like to know your opinion.


One of those systems is Slack for Mac. Starting at 2021/05/05 05:30 UTC, Slack client no longer correctly displays previews of images when the filename is "local.path".

Expectation Reality
Screenshot 2021-05-07 21 52 06 Screenshot 2021-05-07 21 47 43

The relevant code of this issue is here.
https://github.com/socketry/multipart-post/blob/master/lib/multipart/post/composite_read_io.rb#L80-L88

Allow UploadIO to accept additional MIME headers as options

The UploadIO initializer accepts a list of options and uses them to construct MIME headers, but it only pulls out headers it knows about. It would be nice to be able to include arbitrary headers.

For an example use case, see the Packaging and Content-MD5 headers in the SWORD 2.0 spec, e.g.

Content-Type: application/zip
Content-Disposition: attachment; name=payload; filename=[filename]
Packaging: http://purl.org/net/sword/package/SimpleZip
Content-MD5: [md5-digest]
MIME-Version: 1.0

...binary package data...

Errno::ENOENT in multipart-post-1.0.1/lib/parts.rb:42

when POST a.jpg to server with new name 'a'.
it will raise:

/home/gavin_kou/local/lib/ruby/gems/1.8/gems/multipart-post-1.0.1/lib/parts.rb:42:in `size': No such file or directory - 2 (Errno::ENOENT)
        from /home/gavin_kou/local/lib/ruby/gems/1.8/gems/multipart-post-1.0.1/lib/parts.rb:42:in`initialize'
        from /home/gavin_kou/local/lib/ruby/gems/1.8/gems/multipart-post-1.0.1/lib/parts.rb:5:in `new'
        from /home/gavin_kou/local/lib/ruby/gems/1.8/gems/multipart-post-1.0.1/lib/parts.rb:5:in`new'
        from /home/gavin_kou/local/lib/ruby/gems/1.8/gems/multipart-post-1.0.1/lib/multipartable.rb:6:in `initialize'
        from /home/gavin_kou/local/lib/ruby/gems/1.8/gems/multipart-post-1.0.1/lib/composite_io.rb:76:in`map'
        from /home/gavin_kou/local/lib/ruby/gems/1.8/gems/multipart-post-1.0.1/lib/multipartable.rb:6:in `each'
        from /home/gavin_kou/local/lib/ruby/gems/1.8/gems/multipart-post-1.0.1/lib/multipartable.rb:6:in`map'
        from /home/gavin_kou/local/lib/ruby/gems/1.8/gems/multipart-post-1.0.1/lib/multipartable.rb:6:in `initialize'

in parts.rb line 41. change from:

file_length = io.respond_to?(:length) ?  io.length : File.size(io.local_path)

to

file_length = io.respond_to?(:length) ?  io.length : File.size(io.path)

it works fine.

Issues with darkfish docs?

I found out about multipart-post as a library dependency to r10k for puppet.
gem2rpm does not work with multipart-post because of a documentation issue that I would like to raise awareness on.

Normally people would bypass the errors below with --no-ri but gem2rpm only supports --no-doc, so the alternative is to ask "Why?" and push for a fix. Seems to be caused by an undefined/unsupported formatter called darkfish? Anyone know for sure?

+ umask 022
+ cd /tmp/mk-gem2rpm/rpmbuild/BUILD
+ cd multipart-post-2.0.0
+ LANG=C
+ export LANG
+ unset DISPLAY
+ gem build multipart-post.gemspec
  Successfully built RubyGem
  Name: multipart-post
  Version: 2.0.0
  File: multipart-post-2.0.0.gem
+ mkdir -p ./usr/lib/ruby/gems/1.8
+ CONFIGURE_ARGS='--with-cflags='\''-O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector --param=ssp-buffer-size=4 -m64 -mtune=generic'\'' '
+ gem install -V --local --install-dir ./usr/lib/ruby/gems/1.8 --bindir ./usr/bin --force multipart-post-2.0.0.gem
Installing gem multipart-post-2.0.0
Using local gem /tmp/mk-gem2rpm/rpmbuild/BUILD/multipart-post-2.0.0/usr/lib/ruby/gems/1.8/cache/multipart-post-2.0.0.gem
/tmp/mk-gem2rpm/rpmbuild/BUILD/multipart-post-2.0.0/usr/lib/ruby/gems/1.8/gems/multipart-post-2.0.0/.gitignore
/tmp/mk-gem2rpm/rpmbuild/BUILD/multipart-post-2.0.0/usr/lib/ruby/gems/1.8/gems/multipart-post-2.0.0/.travis.yml
/tmp/mk-gem2rpm/rpmbuild/BUILD/multipart-post-2.0.0/usr/lib/ruby/gems/1.8/gems/multipart-post-2.0.0/Gemfile
/tmp/mk-gem2rpm/rpmbuild/BUILD/multipart-post-2.0.0/usr/lib/ruby/gems/1.8/gems/multipart-post-2.0.0/History.txt
/tmp/mk-gem2rpm/rpmbuild/BUILD/multipart-post-2.0.0/usr/lib/ruby/gems/1.8/gems/multipart-post-2.0.0/Manifest.txt
/tmp/mk-gem2rpm/rpmbuild/BUILD/multipart-post-2.0.0/usr/lib/ruby/gems/1.8/gems/multipart-post-2.0.0/README.md
/tmp/mk-gem2rpm/rpmbuild/BUILD/multipart-post-2.0.0/usr/lib/ruby/gems/1.8/gems/multipart-post-2.0.0/Rakefile
/tmp/mk-gem2rpm/rpmbuild/BUILD/multipart-post-2.0.0/usr/lib/ruby/gems/1.8/gems/multipart-post-2.0.0/lib/composite_io.rb
/tmp/mk-gem2rpm/rpmbuild/BUILD/multipart-post-2.0.0/usr/lib/ruby/gems/1.8/gems/multipart-post-2.0.0/lib/multipart_post.rb
/tmp/mk-gem2rpm/rpmbuild/BUILD/multipart-post-2.0.0/usr/lib/ruby/gems/1.8/gems/multipart-post-2.0.0/lib/multipartable.rb
/tmp/mk-gem2rpm/rpmbuild/BUILD/multipart-post-2.0.0/usr/lib/ruby/gems/1.8/gems/multipart-post-2.0.0/lib/net/http/post/multipart.rb
/tmp/mk-gem2rpm/rpmbuild/BUILD/multipart-post-2.0.0/usr/lib/ruby/gems/1.8/gems/multipart-post-2.0.0/lib/parts.rb
/tmp/mk-gem2rpm/rpmbuild/BUILD/multipart-post-2.0.0/usr/lib/ruby/gems/1.8/gems/multipart-post-2.0.0/multipart-post.gemspec
/tmp/mk-gem2rpm/rpmbuild/BUILD/multipart-post-2.0.0/usr/lib/ruby/gems/1.8/gems/multipart-post-2.0.0/test/multibyte.txt
/tmp/mk-gem2rpm/rpmbuild/BUILD/multipart-post-2.0.0/usr/lib/ruby/gems/1.8/gems/multipart-post-2.0.0/test/net/http/post/test_multipart.rb
/tmp/mk-gem2rpm/rpmbuild/BUILD/multipart-post-2.0.0/usr/lib/ruby/gems/1.8/gems/multipart-post-2.0.0/test/test_composite_io.rb
/tmp/mk-gem2rpm/rpmbuild/BUILD/multipart-post-2.0.0/usr/lib/ruby/gems/1.8/gems/multipart-post-2.0.0/test/test_parts.rb
Successfully installed multipart-post-2.0.0
1 gem installed
Installing ri documentation for multipart-post-2.0.0...
rdoc --ri --op /tmp/mk-gem2rpm/rpmbuild/BUILD/multipart-post-2.0.0/usr/lib/ruby/gems/1.8/doc/multipart-post-2.0.0/ri --main README.md -SHN -f darkfish --quiet lib --title multipart-post-2.0.0 Documentation

Invalid output formatter

For help on options, try 'rdoc --help'

error: Bad exit status from /var/tmp/rpm-tmp.GTcHk9 (%build)


RPM build errors:
    Bad exit status from /var/tmp/rpm-tmp.GTcHk9 (%build)
/tmp/mk-gem2rpm/gems/cache/semantic_puppet-0.1.1.gem
executing: gem2rpm -t spec.template /tmp/mk-gem2rpm/gems/cache/semantic_puppet-0.1.1.gem > /tmp/mk-gem2rpm/rpmbuild/SPECS/semantic_puppet-0.1.1.spec
executing: rpmbuild  --define '_unpackaged_files_terminate_build 0' --define '_topdir /tmp/mk-gem2rpm/rpmbuild' -ba /tmp/mk-gem2rpm/rpmbuild/SPECS/semantic_puppet-0.1.1.spec 
Executing(%prep): /bin/sh -e /var/tmp/rpm-tmp.zqNALv
# cat /var/tmp/rpm-tmp.GTcHk9
#!/bin/sh

  RPM_SOURCE_DIR="/tmp/mk-gem2rpm/rpmbuild/SOURCES"
  RPM_BUILD_DIR="/tmp/mk-gem2rpm/rpmbuild/BUILD"
  RPM_OPT_FLAGS="-O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector --param=ssp-buffer-size=4 -m64 -mtune=generic"
  RPM_ARCH="x86_64"
  RPM_OS="linux"
  export RPM_SOURCE_DIR RPM_BUILD_DIR RPM_OPT_FLAGS RPM_ARCH RPM_OS
  RPM_DOC_DIR="/usr/share/doc"
  export RPM_DOC_DIR
  RPM_PACKAGE_NAME="rubygem-multipart-post"
  RPM_PACKAGE_VERSION="2.0.0"
  RPM_PACKAGE_RELEASE="1.el6"
  export RPM_PACKAGE_NAME RPM_PACKAGE_VERSION RPM_PACKAGE_RELEASE
  LANG=C
  export LANG
  unset CDPATH DISPLAY ||:
  RPM_BUILD_ROOT="/tmp/mk-gem2rpm/rpmbuild/BUILDROOT/rubygem-multipart-post-2.0.0-1.el6.x86_64"
  export RPM_BUILD_ROOT

  PKG_CONFIG_PATH="${PKG_CONFIG_PATH}:/usr/lib64/pkgconfig:/usr/share/pkgconfig"
  export PKG_CONFIG_PATH

  set -x
  umask 022
  cd "/tmp/mk-gem2rpm/rpmbuild/BUILD"
cd 'multipart-post-2.0.0'
LANG=C
export LANG
unset DISPLAY

# Create the gem as gem install only works on a gem file
gem build multipart-post.gemspec

# %gem_install compiles any C extensions and installs the gem into ./%gem_dir
# by default, so that we can move it into the buildroot in %install

mkdir -p ./usr/lib/ruby/gems/1.8 

CONFIGURE_ARGS="--with-cflags='-O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector --param=ssp-buffer-size=4 -m64 -mtune=generic' $CONFIGURE_ARGS" \
gem install \
        -V \
        --local \
        --install-dir ./usr/lib/ruby/gems/1.8 \
        --bindir ./usr/bin \
        --force \
        multipart-post-2.0.0.gem

not able to run the tests

ajinkya@ajinkya-Inspiron-N5010:~$ gem2deb multipart-post
multipart-post doesn't seem to exist. Let's try to download it with 'gem fetch multipart-post'
gem fetch multipart-post
Fetching: multipart-post-1.1.5.gem (100%)
Downloaded multipart-post-1.1.5
-- Creating source tarball from multipart-post-1.1.5.gem ...
tar xfm /home/ajinkya/multipart-post-1.1.5.gem
tar xzfm data.tar.gz
zcat metadata.gz > metadata.yml
tar czf /home/ajinkya/multipart-post-1.1.5.tar.gz multipart-post-1.1.5
-- Successfully created multipart-post-1.1.5.tar.gz

-- Creating Debian source package from multipart-post-1.1.5.tar.gz ...
tar xzf ruby-multipart-post_1.1.5.orig.tar.gz
-- Generated Debian source tree in ruby-multipart-post-1.1.5

-- Building Debian package ...
dpkg-buildpackage -us -uc
dpkg-buildpackage: export CFLAGS from dpkg-buildflags (origin: vendor): -g -O2 -fstack-protector --param=ssp-buffer-size=4 -Wformat -Wformat-security
dpkg-buildpackage: export CPPFLAGS from dpkg-buildflags (origin: vendor): -D_FORTIFY_SOURCE=2
dpkg-buildpackage: export CXXFLAGS from dpkg-buildflags (origin: vendor): -g -O2 -fstack-protector --param=ssp-buffer-size=4 -Wformat -Wformat-security
dpkg-buildpackage: export FFLAGS from dpkg-buildflags (origin: vendor): -g -O2
dpkg-buildpackage: export LDFLAGS from dpkg-buildflags (origin: vendor): -Wl,-Bsymbolic-functions -Wl,-z,relro
dpkg-buildpackage: source package ruby-multipart-post
dpkg-buildpackage: source version 1.1.5-1
dpkg-buildpackage: source changed by ajinkya ajinkya@ajinkya-Inspiron-N5010
dpkg-buildpackage: host architecture i386
dpkg-source --before-build ruby-multipart-post-1.1.5
fakeroot debian/rules clean
dh clean --buildsystem=ruby --with ruby
dh_testdir -O--buildsystem=ruby
dh_auto_clean -O--buildsystem=ruby
Entering dh_ruby --clean
Leaving dh_ruby --clean
dh_clean -O--buildsystem=ruby
dpkg-source -b ruby-multipart-post-1.1.5
dpkg-source: info: using source format 3.0 (quilt)' dpkg-source: info: building ruby-multipart-post using existing ./ruby-multipart-post_1.1.5.orig.tar.gz dpkg-source: info: building ruby-multipart-post in ruby-multipart-post_1.1.5-1.debian.tar.gz dpkg-source: info: building ruby-multipart-post in ruby-multipart-post_1.1.5-1.dsc debian/rules build dh build --buildsystem=ruby --with ruby dh_testdir -O--buildsystem=ruby dh_auto_configure -O--buildsystem=ruby dh_auto_build -O--buildsystem=ruby dh_auto_test -O--buildsystem=ruby fakeroot debian/rules binary dh binary --buildsystem=ruby --with ruby dh_testroot -O--buildsystem=ruby dh_prep -O--buildsystem=ruby dh_installdirs -O--buildsystem=ruby dh_auto_install -O--buildsystem=ruby Entering dh_ruby --install install -d /home/ajinkya/ruby-multipart-post-1.1.5/debian/ruby-multipart-post/usr/lib/ruby/vendor_ruby install -D -m644 lib/composite_io.rb /home/ajinkya/ruby-multipart-post-1.1.5/debian/ruby-multipart-post/usr/lib/ruby/vendor_ruby/composite_io.rb install -D -m644 lib/net/http/post/multipart.rb /home/ajinkya/ruby-multipart-post-1.1.5/debian/ruby-multipart-post/usr/lib/ruby/vendor_ruby/net/http/post/multipart.rb install -D -m644 lib/multipartable.rb /home/ajinkya/ruby-multipart-post-1.1.5/debian/ruby-multipart-post/usr/lib/ruby/vendor_ruby/multipartable.rb install -D -m644 lib/multipart_post.rb /home/ajinkya/ruby-multipart-post-1.1.5/debian/ruby-multipart-post/usr/lib/ruby/vendor_ruby/multipart_post.rb install -D -m644 lib/parts.rb /home/ajinkya/ruby-multipart-post-1.1.5/debian/ruby-multipart-post/usr/lib/ruby/vendor_ruby/parts.rb /usr/bin/ruby1.8 -I/usr/lib/ruby/vendor_ruby /usr/lib/ruby/vendor_ruby/gem2deb/test_runner.rb Running tests for ruby1.8 with test file list from debian/ruby-test-files.yaml ... -e:1:inrequire': no such file to load -- test/multibyte.txt (LoadError)
from -e:1
from -e:1:in `each'
from -e:1

Test "ruby1.8" failed. Continue building the package? (Y/N)

Release a 2.2.0

  • CHANGELOG items from v2.1.1...master - if you see something that's a feature, or notable, make a comment here, so we can source a CHANGELOG section for this version.
  • bump minor (there are tiny features)
  • release the gem

UploadIO doesn't work for StringIOs in Ruby 1.9.2

In Ruby 1.8.7, StringIO responds to path, but in 1.9.2, it no longer does, which causes UploadIO to break (composite_io.rb:69).

An easy solution would be to check for obj.respond_to? :path ? obj.path : "no local path" (in my tests, supplying a dummy local path seems to work), but I don't know if that causes problems down the line. If not, one of us could write a pull request.

Thanks for the great gem!

Alex

Can I upload multiple files with one request?

I want to upload 2 files within one request.

Is there a way to do this with your lib?

There is an example in Java. It seems like I need to upload a json file and an attachment in this case.

Couldn't figure out how to do it. Do you have a hint?

JAVA EXAMPLE:

File imageFile = new File("/path/imageToUpload.jpeg"); File metadataFile = new File("/path/metadataToUpload.json");
PostMethod filePost = new PostMethod("http://host/../attachment");
Part[] parts = {
      new FilePart(โ€œmetadataโ€, metadataFile, โ€œapplication/jsonโ€, "UTF8"),
      new FilePart(โ€œattachmentโ€, imageFile, โ€œimage/jpegโ€, null);
  };
 filePost.setRequestEntity(
      new MultipartRequestEntity(parts, filePost.getParams())  ); HttpClient client = new HttpClient(); int status = client.executeMethod(filePost);

Regards,
Christian

Specify correct Ruby version required in 2.2.0

In v2.2.0 (RubyGems.org v2.2.0), dropped Ruby 2.2 support due to #91.

Specify the version in the gemspec. @ioquatix thoughts?

From 83cd51e3fe6e1e2be1375602274caa6ae0ac2a86 Mon Sep 17 00:00:00 2001
From: nobu <nobu@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>
Date: Thu, 30 Jul 2015 04:20:00 +0000
Subject: [PATCH] variable.c: Module#deprecate_constant

* variable.c (rb_const_get_0): warn deprecated constant reference.
* variable.c (rb_mod_deprecate_constant): mark constants to be
  warned as deprecated.  [Feature #11398]

git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@51444 b2dd03c8-39d4-4d8f-98ff-823fe69b080e

Deprecations aren't required

I ended up updating to the new constants, but I thought I'd let you know that the deprecations aren't working as likely intended as they do not appear to be required when loading multipart/post.

Timeout on JRuby on Windows

I am experiencing an issue in multipart-post 2.0.0. I have a client piece of code that looks roughly like this:

require 'net/http'
require 'net/http/post/multipart'

http = Net::HTTP.new('localhost', 8080)

http.start do |http|
  File.open('C:/data/asd.png') do |png|
    req = Net::HTTP::Post::Multipart.new('http://localhost:8080/some-endpoint',
      'uploadId' => "some-internal-data",
      'files[]' => UploadIO.new(png, 'image/png', 'asd.png'),
      'size' => 186670,
      )

    # Some headers for session ID and CSRF protection
    req['Cookie'] = "JSESSIONID=1234567890abcdef"
    req['X-Requested-By'] = 'UNUSED'

    http.request(req)
  end
end

The code runs on JRuby 9.1.6.0 on Windows 10. When I run it I get this stack trace:

Net::ReadTimeout: Net::ReadTimeout
        from C:/dev/jruby-9.1.6.0/lib/ruby/stdlib/net/protocol.rb:158:in `rbuf_fill'
        from C:/dev/jruby-9.1.6.0/lib/ruby/stdlib/net/protocol.rb:136:in `readuntil'
        from C:/dev/jruby-9.1.6.0/lib/ruby/stdlib/net/protocol.rb:146:in `readline'
        from C:/dev/jruby-9.1.6.0/lib/ruby/stdlib/net/http/response.rb:40:in `read_status_line'
        from C:/dev/jruby-9.1.6.0/lib/ruby/stdlib/net/http/response.rb:29:in `read_new'
        from C:/dev/jruby-9.1.6.0/lib/ruby/stdlib/net/http.rb:1448:in `block in transport_request'
        from org/jruby/RubyKernel.java:1118:in `catch'
        from C:/dev/jruby-9.1.6.0/lib/ruby/stdlib/net/http.rb:1445:in `transport_request'
        from C:/dev/jruby-9.1.6.0/lib/ruby/stdlib/net/http.rb:1418:in `request'
        from (irb):19:in `block in evaluate'
        from org/jruby/RubyIO.java:1141:in `open'
        from (irb):11:in `block in evaluate'
        from C:/dev/jruby-9.1.6.0/lib/ruby/stdlib/net/http.rb:858:in `start'
        from (irb):10:in `<eval>'
        from org/jruby/RubyKernel.java:998:in `eval'
        from org/jruby/RubyKernel.java:1299:in `loop'
        from org/jruby/RubyKernel.java:1118:in `catch'
        from org/jruby/RubyKernel.java:1118:in `catch'
        from jirb:13:in `<main>'

On the server side there is a Java + Tomcat on Windows. The output from the server is java.net.SocketTimeoutException: null. The stack trace on the server is most likely irrelevant.

I've tried identical code on a JRuby/Linux client with the exact same Java/Windows server and it worked. Furthermore - I tried MRI/Windows client and it worked too. I also tried some older builds of JRuby I have locally and they failed. So there appears to be some incompatibility in multipart-post with JRuby on Windows.

I'm not even sure if I should be logging this here. Still it's probably good to have it here so that it's visible for users of the library. Is there something that can be done other than forwarding this to the JRuby guys?

Ditch That Hoe / You seem to have contracted the Hoe virus.

Hi, you seem to have Hoe as a runtime dependancy.

As such I have ended up just copying your code into my gem, as opposed to having multipart-post as a dependancy. While it's ok for me to install hoe toi use your gem directly, I can't justify forcing my downstream users to install it.

If you could either git it set up as a development only dependancy, or just use gem-this or similar, that would be just great.

Thanks and sorry about the puns.

Silence Noisy Deprecation Warnings

Can there be an option to silence deprecation warnings? This is the output when running grosser/parallel_tests

Top level ::CompositeIO is deprecated, require 'multipart/post' and use `Multipart::Post::CompositeReadIO` instead!
Top level ::CompositeIO is deprecated, require 'multipart/post' and use `Multipart::Post::CompositeReadIO` instead!
Top level ::CompositeIO is deprecated, require 'multipart/post' and use `Multipart::Post::CompositeReadIO` instead!
Top level ::Parts is deprecated, require 'multipart/post' and use `Multipart::Post::Parts` instead!
Top level ::CompositeIO is deprecated, require 'multipart/post' and use `Multipart::Post::CompositeReadIO` instead!
Top level ::Parts is deprecated, require 'multipart/post' and use `Multipart::Post::Parts` instead!
Top level ::CompositeIO is deprecated, require 'multipart/post' and use `Multipart::Post::CompositeReadIO` instead!
Top level ::CompositeIO is deprecated, require 'multipart/post' and use `Multipart::Post::CompositeReadIO` instead!
Top level ::Parts is deprecated, require 'multipart/post' and use `Multipart::Post::Parts` instead!
Top level ::Parts is deprecated, require 'multipart/post' and use `Multipart::Post::Parts` instead!

NoMethodError: undefined method `alphanumeric' for SecureRandom:Module

If you're using multipart-post version 2.1.0 with ruby version 2.4.1p111 you'll receive the following exception:

NoMethodError: undefined method `alphanumeric' for SecureRandom:Module

This is because SecureRandom.alphanumeric was added to ruby in version 2.5.0 (see ruby/ruby@b867882 and https://github.com/ruby/ruby/blob/v2_5_0/NEWS).

Ruby 2.4 is still supported by the ruby language team.

Currently you have to downgrade multipart-post to version 2.0.0, or upgrade the ruby to at least version 2.5.0.

See a8bccb9 for the introducing commit.

CI needed

Travis ended, what do we want to use next? GitHub Actions?

File upload is relatively slow

Sir,
Although this is not really an issue, at least not a major one, I would like to report that file upload is relatively slow , approximately only one third of my maximal connexion speed (which is attained when using for instance Firefox).
This is on a machine running WinXP(sp3) and Ruby 1.9. I used the code you provide as an example with two slight modifications: sslv3 and a custom header containing an Oauth2 "access token".
Note that I previously noted that "streamed" multi part uploads with Python were somewhat slower than when the file is loaded in memory, but not to the same extend.
Do you have any idea of how upload could be made faster while avoiding high memory usage?

Namespace conflict issue after 2.2.0

This is not global issue, maybe a kind of case report.

2.2.0 contains big refactoring (#65) and it makes sense. However compatibility code made an issue on my code.

I have Parts module,

module Parts
  class Foo; end
end

And with 2.1.1, it works fine.

$ bundle exec irb
2.7.2 :001 > require_relative 'parts/foo'
 => true 
2.7.2 :002 > require 'net/http/post/multipart'
 => true 
2.7.2 :003 > Parts::Foo.class
 => Class 
2.7.2 :004 > Parts.constants
 => [:Foo, :Part, :EpiloguePart, :FilePart, :ParamPart] 

However, with 2.2.0, it have loading order issue.

When load modules on following order, it works.

  1. Multipart gem
  2. my Parts module
2.7.2 :001 > require 'net/http/post/multipart'
 => true 
2.7.2 :003 > Multipart::Post::Parts.class
 => Module 
2.7.2 :006 > require_relative 'parts/foo'
 => true 
2.7.2 :007 > Parts.class
 => Module 
2.7.2 :011 >  Parts.constants
 => [:Foo] 

But when following order, it does not work.

  1. my Parts module
  2. Multipart gem
2.7.2 :001 > require_relative 'parts/foo'
 => true 
2.7.2 :002 > require 'net/http/post/multipart'
/usr/local/rvm/gems/ruby-2.7.2/gems/multipart-post-2.2.3/lib/multipart/post/parts.rb:151: warning: already initialized constant Parts
/workspaces/sandbox-ruby/simple/parts/foo.rb:3: warning: previous definition of Parts was here
 => true                                                            
2.7.2 :003 > Parts::Foo
Traceback (most recent call last):
        21: from /usr/local/rvm/gems/ruby-2.7.2/bin/ruby_executable_hooks:22:in `<main>'
        20: from /usr/local/rvm/gems/ruby-2.7.2/bin/ruby_executable_hooks:22:in `eval'
        19: from /usr/local/rvm/gems/ruby-2.7.2/bin/irb:23:in `<main>'
        18: from /usr/local/rvm/gems/ruby-2.7.2/bin/irb:23:in `load'
        17: from /usr/local/rvm/gems/ruby-2.7.2/gems/irb-1.4.1/exe/irb:11:in `<top (required)>'
(irb):3:in `<main>': uninitialized constant Multipart::Post::Parts::Foo (NameError)

I guess it led by here

Parts = Multipart::Post::Parts
, and it is for compatibility.

If that part of code change, it would be breaking change. so, I'm not sure how it should be fixed.

Do you have any ideas? Like applying a monkey patch?

Multipart post with a zip file

I want to make a post request with faraday with a zip file and it fails with multipart-post.

I have this code :

   conn = Faraday.new(:url => "mon_url") do |conn|
          # POST/PUT params encoders:
        conn.request :multipart
        conn.request :url_encoded

        conn.adapter :net_http
    end

    file = StringIO.new( { "parameters" => {
                                     "neptune-import" => {
                                       "no_save" => false,
                                       "user_name" => "user",
                                       "name" => "Mon test",
                                       "organisation_name" => "organisation",
                                       "referential_name" => "test",
                                     }
                                   }
                  }.to_s )
    action_params_io = Faraday::UploadIO.new(file, "application/json", "parameters.json")

    transport_data_io = Faraday::UploadIO.new(::Zip::File.open("demo.zip"), "application/zip", "demo.zip")

    payload = {
                                                  :file1 => action_params_io,
                                                  :file2 => transport_data_io,
                        }

conn.post "http://", payload 

And it returns an error on length method :

NoMethodError: undefined method `length' for #<Zip::File:0x007f0cb455eae8>
    from /home/luc/.rbenv/versions/2.1.3/lib/ruby/gems/2.1.0/gems/multipart-post-2.0.0/lib/composite_io.rb:102:in `method_missing'
    from (irb):47

Do you have any idea how I could do that with your gem?

Thanks for your time and your work
Luc Donnet

Errno::ENOENT: No such file or directory - local.path

I'm using Faraday which requires this gem as a dependency. When I try this code in the rails console

conn = Faraday.new('http://localhost:3000') do |far|
  far.request :multipart
  far.request :url_encoded
end
params = {}
params[:user] = {:avatar => Faraday::UploadIO.new('sample.jpg', 'image/jpeg') }
response = conn.put('/users', params)

I get the following error:

Errno::ENOENT: No such file or directory - local.path
    from /Users/jeremy/.rvm/gems/ruby-1.9.3-p362/gems/multipart-post-1.1.5/lib/parts.rb:51:in `size'
    from /Users/jeremy/.rvm/gems/ruby-1.9.3-p362/gems/multipart-post-1.1.5/lib/parts.rb:51:in `initialize'
    from /Users/jeremy/.rvm/gems/ruby-1.9.3-p362/gems/multipart-post-1.1.5/lib/parts.rb:11:in `new'
    from /Users/jeremy/.rvm/gems/ruby-1.9.3-p362/gems/multipart-post-1.1.5/lib/parts.rb:11:in `new'
    from /Users/jeremy/.rvm/gems/ruby-1.9.3-p362/gems/faraday-0.8.4/lib/faraday/request/multipart.rb:37:in `block in create_multipart'
    from /Users/jeremy/.rvm/gems/ruby-1.9.3-p362/gems/faraday-0.8.4/lib/faraday/request/multipart.rb:57:in `call'
    from /Users/jeremy/.rvm/gems/ruby-1.9.3-p362/gems/faraday-0.8.4/lib/faraday/request/multipart.rb:57:in `block in process_params'
    from /Users/jeremy/.rvm/gems/ruby-1.9.3-p362/gems/faraday-0.8.4/lib/faraday/request/multipart.rb:47:in `each'
    from /Users/jeremy/.rvm/gems/ruby-1.9.3-p362/gems/faraday-0.8.4/lib/faraday/request/multipart.rb:47:in `inject'
    from /Users/jeremy/.rvm/gems/ruby-1.9.3-p362/gems/faraday-0.8.4/lib/faraday/request/multipart.rb:47:in `process_params'
    from /Users/jeremy/.rvm/gems/ruby-1.9.3-p362/gems/faraday-0.8.4/lib/faraday/request/multipart.rb:55:in `block in process_params'
    from /Users/jeremy/.rvm/gems/ruby-1.9.3-p362/gems/faraday-0.8.4/lib/faraday/request/multipart.rb:47:in `each'
    from /Users/jeremy/.rvm/gems/ruby-1.9.3-p362/gems/faraday-0.8.4/lib/faraday/request/multipart.rb:47:in `inject'
    from /Users/jeremy/.rvm/gems/ruby-1.9.3-p362/gems/faraday-0.8.4/lib/faraday/request/multipart.rb:47:in `process_params'
    from /Users/jeremy/.rvm/gems/ruby-1.9.3-p362/gems/faraday-0.8.4/lib/faraday/request/multipart.rb:36:in `create_multipart'
    from /Users/jeremy/.rvm/gems/ruby-1.9.3-p362/gems/faraday-0.8.4/lib/faraday/request/multipart.rb:11:in `block in call'
    from /Users/jeremy/.rvm/gems/ruby-1.9.3-p362/gems/faraday-0.8.4/lib/faraday/request/url_encoded.rb:20:in `match_content_type'
    from /Users/jeremy/.rvm/gems/ruby-1.9.3-p362/gems/faraday-0.8.4/lib/faraday/request/multipart.rb:7:in `call'
    from /Users/jeremy/.rvm/gems/ruby-1.9.3-p362/gems/faraday-0.8.4/lib/faraday/connection.rb:226:in `run_request'
    from /Users/jeremy/.rvm/gems/ruby-1.9.3-p362/gems/faraday-0.8.4/lib/faraday/connection.rb:99:in `put'
    from (irb):60                               
    from /Users/jeremy/.rvm/gems/ruby-1.9.3-p362/gems/railties-3.2.11/lib/rails/commands/console.rb:47:in `start'
    from /Users/jeremy/.rvm/gems/ruby-1.9.3-p362/gems/railties-3.2.11/lib/rails/commands/console.rb:8:in `start'
    from /Users/jeremy/.rvm/gems/ruby-1.9.3-p362/gems/railties-3.2.11/lib/rails/commands.rb:41:in `<top (required)>'
    from script/rails:6:in `require'
    from script/rails:6:in `<main>'1.9.3p362 :061 >

Using version 1.1.5

Can't debug an http result

Hi, first of all thanks for this gem.

I'm trying to make it work but I don't see the uploaded file at my server. It may be related to several things, and, in order to debug, I wanted to see the request information, for example whether it's a 404 not found or a 403 not allowed.

When I do this: puts "Result: #{res}", nothing is echoed. Is there anyway to debug the result of a request.

Thanks, Diego.

Errno::EPIPE: Broken pipe

gem version: 1.1.5

I have the following method (for reproducing)

require 'net/http/post/multipart'

def submit_file
  f = './lib/qa_monitoring/video_source/test.mp4'
  uri = URI.parse('https://api.tout.com/api/v1/touts')
  header = {"Authorization"=>"Bearer some_api_token", "Connection"=>"keep-alive",       "Accept"=>"application/json"}
  File.open(f) do |video|
    req = Net::HTTP::Post::Multipart.new(uri.path,
      {"file" => UploadIO.new(video, 'video/mp4', "video.mp4")},
      header)
    res = Net::HTTP.start(uri.host, uri.port) do |http|
      http.request(req)
    end
  end
  res
end

When I run this in irb, I get the following exception:

Errno::EPIPE: Broken pipe
from /Users/kris/.rvm/rubies/ruby-1.9.3-p362/lib/ruby/1.9.1/net/protocol.rb:199:in write' from /Users/kris/.rvm/rubies/ruby-1.9.3-p362/lib/ruby/1.9.1/net/protocol.rb:199:inwrite0'
from /Users/kris/.rvm/rubies/ruby-1.9.3-p362/lib/ruby/1.9.1/net/protocol.rb:173:in block in write' from /Users/kris/.rvm/rubies/ruby-1.9.3-p362/lib/ruby/1.9.1/net/protocol.rb:190:inwriting'
from /Users/kris/.rvm/rubies/ruby-1.9.3-p362/lib/ruby/1.9.1/net/protocol.rb:172:in write' from /Users/kris/.rvm/rubies/ruby-1.9.3-p362/lib/ruby/1.9.1/net/http.rb:1955:insend_request_with_body_stream'
from /Users/kris/.rvm/rubies/ruby-1.9.3-p362/lib/ruby/1.9.1/net/http.rb:1921:in exec' from /Users/kris/.rvm/rubies/ruby-1.9.3-p362/lib/ruby/1.9.1/net/http.rb:1317:inblock in transport_request'
from /Users/kris/.rvm/rubies/ruby-1.9.3-p362/lib/ruby/1.9.1/net/http.rb:1316:in catch' from /Users/kris/.rvm/rubies/ruby-1.9.3-p362/lib/ruby/1.9.1/net/http.rb:1316:intransport_request'
from /Users/kris/.rvm/rubies/ruby-1.9.3-p362/lib/ruby/1.9.1/net/http.rb:1293:in request' from (irb):12:inblock (2 levels) in submit_file'
from /Users/kris/.rvm/rubies/ruby-1.9.3-p362/lib/ruby/1.9.1/net/http.rb:745:in start' from /Users/kris/.rvm/rubies/ruby-1.9.3-p362/lib/ruby/1.9.1/net/http.rb:557:instart'
from (irb):11:in block in submit_file' from (irb):7:inopen'
from (irb):7:in `submit_file'

With the correct token, I can upload the same file via a REST client.

Does this support nested params?

I want to send some nested params with the file. Is it not getting send properly.

For example

     sub_params = { contact: { 'first_name' => 'prathamesh', 'language' => 'Ruby' } }
     params     = sub_params.merge({ "file" => UploadIO.new(csv, "text/csv", "test.csv") })
     request    = Net::HTTP::Post::Multipart.new(url.path, params)     

On Rails side, i get

Parameters: {"contact"=>"{\"first_name\"=>\"prathamesh\", \"language\"=>\"Ruby\"}", "file"=>#<ActionDispatch::Http::UploadedFile:0x0000000628cea8 ....}

Nested attributes first_name and language are delimited. But they should be not.

Right now, parts are created assuming that value is String. So it is failing here where value is Hash like in case of nested hash.

Force warning

Hello,
I notice that in my project when I use Faraday 0.17.4 which depends on this gem I get warning:

"Top level ::MultipartPost is deprecated, require 'multipart/post' and use Multipart::Post instead!"

I checked the code and see that here does a force warn no matter what. Why is it done in that way?

Tests failing on Windows

After running parts_spec.rb, I get those two tests failing.

test_multibyte_file_length

expected: == 187
     got:    186
./spec/parts_spec.rb:34:in `assert_part_length'
./spec/parts_spec.rb:76:in `block (2 levels) in <top (required)>'

test_multibyte_filename

Errno::EINVAL: Invalid argument @ rb_sysopen - C:/Users/parteau/AppData/Local/Temp/ใƒ•ใ‚กใ‚คใƒซ
20170803-137124-14setfh
./spec/parts_spec.rb:81:in `new'
./spec/parts_spec.rb:81:in `block (2 levels) in <top (required)>'
-e:1:in `load'
-e:1:in `<main>'

OS : Windows Version 10.0.14393
Ruby : ruby 2.3.0p0

Uploaded Image Corrupted with JRuby 1.7.10

For those using JRuby. Image upload may not work with this gem.
Try the following code on JRuby:

f = File.open("test.png")
obj = f.read(f.size)

f.size and obj.size are not the same. It causes content-length and stream size not the same, and image file is corrupted.

support: How to us to upload to S3 (github file upload)

Hi Nick,

This really just a support request (don't think it is a bug) ... if you have time to answer or sample code to point me to.

I'm working on making a script to take my gh-pages branch and upload itto github

They use a two part POST technique described here: http://developer.github.com/v3/repos/downloads/

Here's some of my code -- the first POST using HTTParty works and I get back valid json.

class Github
  include HTTParty
  base_uri 'https://api.github.com/repos/concord-consortium/lab'
  format :json
  basic_auth CONFIG[:username], CONFIG[:password]
  debug_output
end

tag = `git log -1 --date=short --format=%cd-%h gh-pages`.strip
name = "lab-dist-#{tag}.tar.gz"
if File.exist?("uploads/#{name}") || system("git archive gh-pages | gzip >uploads/#{name}")
  options = {
    "name" => name,
    "size" => File.stat("uploads/#{name}").size,
    "description" => "Latest release of Lab distribution",
    "content_type" => "application/x-gzip"
  }
  response = Github.post('/downloads', :body => MultiJson.encode(options))
  pp response

This is the part that doesn't work:

url = URI.parse(response['s3_url'])
File.open("#{PROJECT_ROOT}/uploads/#{name}") do |archive|
  req = Net::HTTP::Post::Multipart.new url.path,
    'key'            => response['path'],
    'acl'            => response['acl'],
    'success_action_status' => 201,
    'Filename'       => response['name'],
    'AWSAccessKeyId' => response['accesskeyid'],
    'Policy'         => response['policy'],
    'Signature'      => response['signature'],
    'Content-Type'   => response['mime_type'],
    "file"           => UploadIO.new(archive, response['mime_type'])
  res = Net::HTTP.start(url.host, url.port) do |http|
    http.request(req)
  end
end

I'm pretty sure thats NOT how to add the extra parameters for the multipart post -- but I didn't see an example combining the file and the other form' elements.

Thanks for any help!

Does multipart-post follow Semantic versioning?

For gems depending on multipart-post that want to add a pessimistic version constraint (e.g. '~> 2.2'), it is useful to know if multipart-post follows the semantic versioning scheme.

I tend to believe it is, but I cannot find it in the README.

EOFError: end of file reached

Hi, I'm new to ruby and I'm trying to use your gem to upload an image to ImageShack. I'm getting this error when I run the code below:

Execution:
MultipartPostTest.new

Source:
require 'rubygems'
require 'net/http/post/multipart'

class MultipartPostTest
def initialize
url = URI.parse('http://www.imageshack.us/upload_api.php')
File.open('c:/x.jpg') do |jpg|
req = Net::HTTP::Post::Multipart.new url.path,
"fileupload" => UploadIO.new(jpg, "image/jpeg", 'c:/x.jpg')
res = Net::HTTP.start(url.host, url.port) do |http|
http.request(req)
end
end
end
end

Trace:
EOFError: end of file reached
from ruby/lib/ruby/1.8/net/protocol.rb:133:in sysread' from ruby/lib/ruby/1.8/net/protocol.rb:133:inrbuf_fill'
from ruby/lib/ruby/1.8/timeout.rb:56:in timeout' from ruby/lib/ruby/1.8/timeout.rb:76:intimeout'
from ruby/lib/ruby/1.8/net/protocol.rb:132:in rbuf_fill' from ruby/lib/ruby/1.8/net/protocol.rb:116:inreaduntil'
from ruby/lib/ruby/1.8/net/protocol.rb:126:in readline' from ruby/lib/ruby/1.8/net/http.rb:2029:inread_status_line'
from ruby/lib/ruby/1.8/net/http.rb:2018:in read_new' from ruby/lib/ruby/1.8/net/http.rb:1059:inrequest'
from test_project/lib/multipart_post_test.rb:11:in initialize' from ruby/lib/ruby/1.8/net/http.rb:547:instart'
from ruby/lib/ruby/1.8/net/http.rb:440:in start' from test_project/lib/multipart_post_test.rb:10:ininitialize'
from test_project/lib/multipart_post_test.rb:7:in open' from test_project/lib/multipart_post_test.rb:7:ininitialize'
from (irb):1:in `new'
from (irb):1>>

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.