Squashed 'third_party/allwpilib/' changes from e4b91005cf..83f1860047

83f1860047 [wpilib] Add/update documentation to PneumaticBase and subclasses (NFC) (#4881)
9872e676d8 [commands] Make Subsystem destructor virtual (#4892)
25db20e49d [hal] Fix segfault in various HAL functions (#4891)
b0c6724eed [glass] Add hamburger menu icon to titlebars (#4874)
f0fa8205ac Add missing compiler flags and fix warnings (#4889)
42fc4cb6bc [wpiutil] SafeThread: Provide start/stop hooks (#4880)
cc166c98d2 [templates] Add Command-based skeleton template (#4861)
3f51f10ad3 [build] Update to 2023v3 image (#4886)
1562eae74a [ntcore] Refactor meta-topic decoding from glass (#4809)
b632b288a3 Fix usages of std::min and std::max to be windows safe (#4887)
c11bd2720f [wpilibc] Add internal function to reset Shuffleboard instance (#4884)
f1151d375f [ntcore] Add method to get server time offset (#4847)
fe1b62647f [hal,wpilib] Update documentation for getComments (NFC) (#4879)
c49a45abbd [build] Fix examples linking in incorrect jni library (#4873)
bc3d01a721 [build] Add platform check to doxygen plugin (#4862)
bc473240ae Add Jetbrains Fleet folder to .gitignore (#4872)
2121bd5fb8 [wpimath] Remove RKF45 (#4870)
835f8470d6 [build] Fix roborio cross-compiler on arm hosts (#4864)
6cfe5de00d [ntcore] Don't deadlock server on early destroy (#4863)
2ac41f3edc [hal, wpilib] Add RobotController.getComments()  (#4463)
26bdbf3d41 Java optimization and formatting fixes (#4857)
92149efa11 Spelling and grammar cleanups (#4849)
176fddeb4c [commands] Add functions to HID classes to allow use of axes as BooleanEvents/Triggers (#4762)
87a34af367 [templates] Add bindings to command-based template (#4838)
4534e75787 [examples] Remove redundant MotorControl example (#4837)
1cbebaa2f7 [commands] Remove final semicolon from test macro definition (#4859)
6efb9ee405 [commands] Add constructor for SwerveControllerCommand that takes a HolonomicDriveController (#4785)
1e7fcd5637 [cscore] Change run loop functions to not be mac specific (#4854)
1f940e2b60 [apriltag] Add C++ wrappers, rewrite Java/JNI to match (#4842)
a6d127aedf [build] Add missing task dependency in wpilibjExamples (#4852)
b893b3d6d3 [cscore] Add support for USB cameras on macOS (#4846)
1696a490fa [glass] Add support for alternate NT ports (#4848)
40a22d69bc [glass] Add support for alternate NT ports (#4848)
e84dbfede0 [wpilib] GenericHID: Add rumble both option (#4843)
8aa9dbfa90 [examples] Link apriltag package in examples build.gradle (#4845)
eda2fa8a17 [build] Update Spotless (#4840)
d20594db0d Fix typos (#4839)
dd8ecfdd54 [commands] Fix typo in waitUntil docs (NFC) (#4841)
17ceebfff4 [apriltag] Clean up apriltag JNI (#4823)
8b74ab389d [examples] RapidReactCommandBot: Fix array indices (#4833)
1aad3489c2 [sim] Implement PD total current and power (#4830)
2744991771 [wpimath] Fix docs in SwerveModulePosition (#4825)
ffbf6a1fa2 [commands] Disable regularly failing unit test (#4824)
fbabd0ef15 [commands] Enhance Command Sendable implementations (#4822)
7713f68772 [hal] Use atomic rather then mutex for DS Data updates (#4787)
701995d6cc [examples] Update Command-based starter project (#4778)
bf7068ac27 [wpilibc] Add missing PPS implementation for C++ (#4821)
aae0f52ca6 [ntcore] NetworkTable: fix visibility of get/set value (#4820)
ee02fb7ba7 [hal] Add support for Pulse-Per-Second signal (#4819)
518916ba02 [wpilib] Fix DS mode thread event being manual reset accidentally (#4818)
3997c6635b [hal] Update to new image, use new TCP notify callback and new duty cycle API (#4774)
cc8675a4e5 [examples] Add comment on how to view elevator sim (NFC) (#4482)
fb2c170b6e [ntcore] Simplify local startup (#4803)
7ba8a9ee1f [wpimath] ProfiledPIDController: Add to SendableRegistry (#4656)
c569d8e523 [wpilib] Joystick.getMagnitude(): use hypot() function (#4816)
2a5e89fa97 [apriltag] Improve description of pose coordinates (NFC) (#4810)
cc003c6c38 [apriltag] Fix AprilTagFieldLayout JSON name (#4814)
5522916123 [commands] CommandXBoxController bumper documentation fix (NFC) (#4815)
967b30de3a [glass] Fix NT view UpdateClients() bug (#4808)
3270d4fc86 [wpimath] Rewrite pose estimator docs (#4807)
be39678447 [apriltag] Add test to ensure apriltagjni loads (#4805)
61c75deb2a [commands] Test no-op behavior of scheduling a scheduled command (#4806)
a865f48e96 [ntcore] Pass pub/sub options as a unified PubSubOptions struct (#4794)
f66a667321 [commands] Fix incorrect Trigger docs (NFC) (#4792)
f8d4e9866e [ntcore] Clean up ntcore_test.h (#4804)
7e84ea891f [wpimath] Fix ComputerVisionUtil transform example in parameter docs (NFC) (#4800)
da3ec1be10 [wpimath] Change terminology for ArmFeedforward gravity gain (NFC) (#4791)
944dd7265d [wpilibc] Add C++ Notifier error handling, update java notifier error message (#4795)
6948cea67a [wpiutil] Fix MemoryBuffer initialization (#4797)
a31459bce6 [wpiutil] Fix UnescapeCString overflow when inputSize < 2 (#4796)
4a0ad6b48c [wpimath] Rotation2d: Add reference to angleModulus in docs (NFC) (#4786)
e6552d272e [ntcore] Remove table multi-subscriber (#4789)
bde383f763 [hal] Replace const char* with std::string_view in Driver Station sim functions (#4532)
5a52b51443 [hal] Add RobotController.getSerialNumber() (#4783)
69a66ec5ec [wpilib] Fix multiple motor safety issues (#4784)
989c9fb29a [wpimath] Revert Rotation2D change that limits angles (#4781)
0f5b08ec69 [wpigui] Update imgui to 1.89.1+ (#4780)
fba191099c [examples] AddressableLED: Add unit test (#4779)
b390cad095 [wpilibj] Consistently use ErrorMessages.requireNonNullParam (#4776)
b9772214d9 [wpilib] Sendable: Don't call setter for getter changes
342c375a71 [ntcore] Add subscriber option to exclude single publisher
b0e4053087 [ntcore] Use int for options instead of double
f3e666b7bb [cscore] Convert YUYV and UYVY directly to grayscale (#4777)
b300518bd1 [hal] Add CAN Stream API to Java through JNI bindings (#4193)
be27171236 [wpilibj] Shuffleboard: Check for null sendable (#4772)
4bbdbdfb48 [commands] Move GroupedCommands to CommandScheduler (#4728)
f18fd41ac3 [wpimath] Remove broken and obsoleted ComputerVisionUtil functions (#4775)
2d0faecf4f [glass] DataSource: Add spinlock to protect value (#4771)
348bd107fc [hal] Add CANManufacturer for The Thrifty Bot (#4773)
3149dc64b8 [examples] HatchbotInlined: Use Subsystem factories (#4765)
8618dd4160 [glass, wpilib] Replace remaining references to Speed Controller with Motor Controller (#4769)
72e21a1ed1 [apriltag] Use wpilibsuite fork of apriltag (#4764)
eab0d929e6 [commands] CommandGenericHID POV methods: Fix docs (NFC) (#4760)
6789869663 [wpilib] Call set(0) rather than disable for stopMotor (#4763)
c9dea2968d [cscore] Emit warning that USB Camera isn't supported on OSX (#4766)
8f402645f5 [commands] Fix PIDSubsystem setSetpoint behavior (#4759)
f24ad1d715 [build] Upgrade to googletest 1.12.1 (#4752)
ff88756864 [wpimath] Add new DCMotor functions for alternative calculations and reduction calculation (#4749)
f58873db8e [wpimath] Remove extra terms in matrix for pose estimator docs (#4756)
37e969b41a [wpimath] Add constructors to pose estimators with default standard deviations (#4754)
13cdc29382 [ci] Rename comment command from "/wpiformat" to "/format" (#4755)
6e23985ae6 [examples] Add main include directory to test builds (#4751)
66bb0ffb2c [examples] Add unit testing infrastructure (#4646)
74cc86c4c5 [wpimath] Make transform tests use pose/transform equality operators (#4675)
e22d8cc343 [wpimath] Use Odometry for internal state in Pose Estimation (#4668)
68dba92630 [ci] Update mac and windows builds to Java 17 (#4750)
23bfc2d9ab [sim] Remove unmaintained Gazebo support (#4736)
1f1461e254 [wpilib] Add method to enable/disable LiveWindow in test mode (#4678)
eae68fc165 [wpimath] Add tolerance for Rotation3d rotation matrix special orthogonality (#4744)
4c4545fb4b [apriltag] Suppress warning (#4743)
16ffaa754d [docs] Generate docs for apriltag subproject (#4745)
5e74ff26d8 [apriltag, build] Update native utils, add apriltag impl and JNI (#4733)
53875419a1 [hal] Allow overriding stderr printing by HAL_SendError (#4742)
aa6499e920 [ntcore] Fix special topic multi-subscriber handling (#4740)
df70351107 [build] Fix cmake install of thirdparty includes (#4741)
e9bd50ff9b [glass] NT view: clear meta-topic info on disconnect (#4732)
9b319fd56b [ntcore] Add sub option for local vs remote changes (#4731)
18d28ec5e3 [ntcore] Remove duplicate value checking from ClientImpl
bdfb625211 [ntcore] Send duplicate values to network if necessary
21003e34eb [commands] Update Subsystem factories and example to return CommandBase (#4729)
70080457d5 [commands] Refactor ProxyScheduleCommand, SelectCommand into ProxyCommand (#4534)
e82cd5147b [wpilib] Tweak Color HSV formula and use in AddressableLED (#4724)
ec124bb662 [commands] Allow unsetting a subsystem's default command (#4621)
2b2aa8eef7 [examples] Update all examples to use NWU coordinate conventions (#4725)
cb38bacfe8 [commands] Revert to original Trigger implementation (#4673)
15561338d5 [commands] Remove one more default command isFinished check (#4727)
ca35a2e097 Add simgui files to .gitignore (#4726)
20dbae0cee [examples] Renovate command-based examples (#4409)
1a59737f40 [commands] Add convenience factories (#4460)
42b6d4e3f7 Use defaulted comparison operators in C++ (#4723)
135c13958f [wpigui] Add FontAwesome (#4713)
ffbfc61532 [ntcore] Add NetworkTable table-specific listeners (#4640)
8958b2a4da [commands] Add property tests for command compositions (#4715)
e4ac09077c [wpilib] Add link to MotorSafety article (#4720)
f40de0c120 [commands] Add C++ factory templates (#4686)
51fa3e851f [build] cmake: Use FetchContent instead of ExternalProject (#4714)
1da84b2255 [wpigui] Reload fonts to scale rather than preloading (#4712)
e43e2fbc84 [wpiutil] StringExtras: Add UnescapeCString (#4707)
5804d8fa84 [ntcore] Server: Properly handle multiple subscribers (#4717)
169ef5fabf [glass] Update NT view for topicsOnly and sendAll changes (#4718)
148759ef54 [examples] CANPDP: Expand properties shown (#4687)
58ed112b51 [commands] RepeatCommand: restart on following iteration (#4706)
dd1da77d20 [readme] Fix broken CI badge (#4710)
7cda85df20 [build] Check Gradle plugin repo last to fix CI (#4711)
7ed9b13277 [build] Bump version plugin to fix null tag (#4705)
6b4f26225d [apriltag] Fix pluralization of apriltag artifacts (#4671)
b2d2924b72 [cscore] Add Y16 image support (#4702)
34ec89c041 [wpilibc] Shuffleboard SimpleWidget: Return pointer instead of reference (#4703)
e15200068d [ci] Disable HW testbench runs (#4704)
d5200db6cd [wpimath] Rename HolonomicDriveController.calculate params (#4683)
2ee3d86de4 [wpimath] Clarify Rotation3d roll-pitch-yaw direction (#4699)
9f0a8b930f [cscore] Use MFVideoFormat_L8 for Gray on Windows (#4701)
2bca43779e [cscore] Add UYVY image support (#4700)
4307d0ee8b [glass] Plot: allow for more than 11 plots (#4685)
3fe8d355a1 [examples] StateSpaceDifferentialDriveSimulation: Use encoder reversed constants (#4682)
b44034dadc [ntcore] Allow duplicate client IDs on server (#4676)
52d2c53888 [commands] Rename Java factory wait() to waitSeconds() (#4684)
76e918f71e [build] Fix JNI artifacts linking to incorrect libraries (#4680)
0bee875aff [commands] Change C++ CommandPtr to use CommandBase (#4677)
98e922313b [glass] Don't check IsConnected for NT widgets (#4674)
9a36373b8f [apriltag] Switch 2022 apriltag layout length and width values (#4670)
cf8faa9e67 [wpilib] Update values on controllable sendables (#4667)
5ec067c1f8 [ntcore] Implement keep duplicates pub/sub flag (#4666)
e962fd2916 [ntcore] Allow numeric-compatible value sets (#4620)
88bd67e7de [ci] Update clang repositories to jammy (#4665)
902e8686d3 [wpimath] Rework odometry APIs to improve feature parity (#4645)
e2d49181da Update to native utils 2023.8.0 (#4664)
149bac55b1 [cscore] Add Arducam OV9281 exposure quirk (#4663)
88f7a3ccb9 [wpimath] Fix Pose relativeTo documentation (#4661)
8acce443f0 [examples] Fix swerve examples to use getDistance for turning encoder (#4652)
295a1f8f3b [ntcore] Fix WaitForListenerQueue (#4662)
388e7a4265 [ntcore] Provide mechanism to reset internals of NT instance (#4653)
13aceea8dc [apriltag] Fix FieldDimensions argument order (#4659)
c203f3f0a9 [apriltag] Fix documentation for AprilTagFieldLayout (#4657)
f54d495c90 Fix non initialized hal functionality during motor safety init (#4658)
e6392a1570 [cmd] Change factories return type to CommandBase (#4655)
53904e7cf4 [apriltag] Split AprilTag functionality to a separate library (#4578)
2e88a496c2 [wpimath] Add support for swerve joystick normalization (#4516)
ce4c45df13 [wpimath] Rework function signatures for Pose Estimation / Odometry (#4642)
0401597d3b [readme] Add wpinet to MavenArtifacts.md (#4651)
2e5f9e45bb [wpimath] Remove encoder reset comments on Swerve, Mecanum Odometry and Pose Estimation (#4643)
e4b5795fc7 [docs] Disable Doxygen for memory to fix search (#4636)
03d0ea188c [build] cmake: Add missing wpinet to installed config file (#4637)
3082bd236b [build] Move version file to its own source set (#4638)
b7ca860417 [build] Use build cache for sign step (#4635)
64838e6367 [commands] Remove unsafe default command isFinished check (#4411)
1269d2b901 [myRobot] Disable spotbugs (#4565)
14d8506b72 [wpimath] Fix units docs for LinearSystemId::IdentifyDrivetrainSystem() (#4600)
d1d458db2b [wpimath] Constrain Rotation2d range to -pi to pi (#4611)
f656e99245 [readme] Add links to development build documentation (#4481)
6dd937cef7 [commands] Fix Trigger API docs (NFC) (#4599)
49047c85b9 [commands] Report error on C++ CommandPtr use-after-move (#4575)
d07267fed1 [ci] Upgrade containers to Ubuntu 22.04 and remove libclang installation (#4633)
b53ce1d3f0 [build, wpiutil] Switch macos to universal binaries (#4628)
5a320c326b [upstream_util, wpiutil] Refactor python scripts (#4614)
c4e526d315 [glass] Fix NT Mechanism2D (#4626)
d122e4254f [ci] Run spotlessApply after wpiformat in comment command (#4623)
5a1e7ea036 [wpilibj] FieldObject2d: Add null check to close() (#4619)
179f569113 [ntcore] Notify locally on SetDefault (#4617)
b0f6dc199d [wpilibc] ShuffleboardComponent.WithProperties: Update type (#4615)
7836f661cd [wpimath] Add missing open curly brace to units/base.h (#4613)
dbcc1de37f [wpimath] Add DifferentialDriveFeedforward classes which wrap LinearPlantInversionFeedforward (#4598)
93890c528b [wpimath] Add additional angular acceleration units (#4610)
3d8d5936f9 [wpimath] Add macro for disabling units fmt support (#4609)
2b04159dec [wpimath] Update units/base.h license header (#4608)
2764004fad [wpinet] Fix incorrect jni definitions (#4605)
85f1bb8f2b [wpiutil] Reenable jni check task (#4606)
231ae2c353 [glass] Plot: Fix Y-axis not being saved (#4594)
e92b6dd5f9 [wpilib] Fix AprilTagFieldLayout JSON property name typos (#4597)
2a8e0e1cc8 Update all dependencies that use grgit (#4596)
7d06e517e9 [commands] Move SelectCommand factory impl to header (#4581)
323524fed6 [wpimath] Remove deprecated units/units.h header (#4572)
d426873ed1 [commands] Add missing PS4 triangle methods (#4576)
5be5869b2f [apriltags] Use map as internal data model (#4577)
b1b4c1e9e7 [wpimath] Fix Pose3d transformBy rotation type (#4545)
a4054d702f [commands] Allow composing two triggers directly (#4580)
0190301e09 [wpilibc] Explicitly mark EventLoop as non-copyable/non-movable (#4579)
9d1ce6a6d9 [ntcore] Catch file open error when saving preferences (#4571)
5005e2ca04 [ntcore] Change Java event mask to EnumSet (#4564)
fa44a07938 [upstream-utils][mpack] Add upstream util for mpack (#4500)
4ba16db645 [ntcore] Various fixes and cleanups (#4544)
837415abfd [hal] Fix joysticks either crashing or returning 0 (#4570)
2c20fd0d09 [wpilib] SingleJointedArmSim: Check angle equals limit on wouldHit (#4567)
64a7136e08 [wpimath] SwerveDrivePoseEstimator: Restore comment about encoder reset (#4569)
b2b473b24a [wpilib] Add AprilTag and AprilTagFieldLayout (#4421)
7aab8fa93a [build] Update to Native Utils 2023.6.0 (#4563)
12c2851856 [commands] WrapperCommand: inherit from CommandBase (#4561)
0da169dd84 [wpimath] Remove template argument from ElevatorFeedforward (#4554)
2416827c25 [wpimath] Fix docs for pose estimator local measurement models (#4558)
1177a3522e [wpilib] Fix Xbox/PS4 POV sim for port number constructors (#4548)
102344e27a [commands] HID classes: Add missing methods, tweak return types (#4557)
1831ef3e19 [wpilib] Fix Shuffleboard SuppliedValueWidget (#4559)
a9606ce870 [wpilib] Fix Xbox/PS4 POV sim (#4546)
6c80d5eab3 [wpimath] Remove unused SymbolExports.h include from units/base.h (#4541)
b114006543 [ntcore] Unify listeners (#4536)
32fbfb7da6 [build] cmake: Install ntcore generated include files (#4540)
02465920fb [build] Update native utils to 2023.4.0 (#4539)
3a5a376465 [wpimath] Increase constexpr support in geometry data types (#4231)
1c3c86e9f1 [ntcore] Cache GetEntry(name) values (#4531)
dcda09f90a [command] Rename trigger methods (#4210)
66157397c1 [wpilib] Make drive classes follow NWU axes convention (#4079)
9e22ffbebf [ntcore] Fix null deref in NT3 client (#4530)
648ab6115c [wpigui,dlt,glass,ov] Support arm in GUI tools (#4527)
8bc3b04f5b [wpimath] Make ComputerVisionUtil use 3D geometry classes (#4528)
cfb84a6083 [wpilibc] Don't hang waiting for NT server to start (#4524)
02c47726e1 [wpimath] Remove unused odometry instance from DifferentialDrivePoseEstimator test (#4522)
b2a0093294 [ci] Revert upgrade of github-pages-deploy-action (#4521)
2a98d6b5d7 [wpimath] PIDController: Add getters for position & velocity tolerances (#4458)
9f36301dc8 [ci] Write wpiformat patch to job summary (#4519)
901fc555f4 [wpimath] Position Delta Odometry for Mecanum (#4514)
4170ec6107 [wpimath] Position Delta Odometry for Swerve (#4493)
fe400f68c5 [docs] Add wpinet to docs build (#4517)
794669b346 [ntcore] Revamp listeners (#4511)
dcfa85a5d5 [ci] Build sanitizers with clang-14 (#4518)
15ad855f1d [ntcore] Add UnitTopic<T> (C++ only) (#4497)
11244a49d9 [wpilib] Add IsConnected function to all gyros (#4465)
1d2e8eb153 [build] Update myRobot deployment (#4515)
ad53fb19b4 [hal] Use new HMB api for addressable LED (#4479)
ba850bac3b [hal] Add more shutdown checks and motor safety shutdown (#4510)
023a5989f8 [ntcore] Fix typo in NetworkServer client connect message (#4512)
c970011ccc [docs] Add Doxygen aliases used by Foonathan memory (#4509)
07a43c3d9a [readme] Document clang-format version and /wpiformat (#4503)
a05b212b04 [ci] Revert changes to wpiformat task from #4501 (#4508)
09faf31b67 [commands] Replace Command HID inheritance with delegation (#4470)
9e1f9c1133 [commands] Add command factories (#4476)
f19d2b9b84 [ci] Add NUMBER environment variable to comment command commit script (#4507)
a28f93863c [ci] Push comment command commit directly to PR (#4506)
c9f61669b8 [ci] Fix comment command commit push (#4505)
dcce5ad3b3 [ci] Update github-script API usage (#4504)
6836e5923d [wpilibc] Restore get duty cycle scale factor (#4502)
335188c652 [dlt] Add deselect/select all buttons to download view (#4499)
60a29dcb99 [glass] Field2D: Add "hidden" option for objects (#4498)
b55d5b3034 [ci] Update deprecated github actions (#4501)
10ed4b3969 [ntcore] Various NT4 fixes (#4474)
4a401b89d7 [hal, wpilib] New DS thread model and implementation (#3787)
c195b4fc46 [wpimath] Clean up PoseEstimator nominal dt docs (#4496)
8f2e34c6a3 [build] Remove wpilib prefix from CMake flat install (#4492)
150d692df7 [wpimath] Remove unused private PoseEstimator function (#4495)
3e5bfff1b5 [wpimath] FromFieldRelativeSpeeds: Add ChassisSpeeds overload (#4494)
9c7e66a27d [commands] C++: Add CommandPtr overload for SetDefaultCommand (#4488)
0ca274866b [build] Fix CMake system library opt-ins (#4487)
dc037f8d41 [commands] Remove EndlessCommand (#4483)
16cdc741cf [wpimath] Add Pose3d(Pose2d) constructor (#4485)
9d5055176d [build] cmake: Allow disabling ntcore build (#4486)
d1e66e1296 [build] Compile all java code with inline string concatenation (#4490)
1fc098e696 Enable log macros to work with no args (#4475)
878cc8defb [wpilib] LiveWindow: Add enableAllTelemetry() (#4480)
8153911160 [build] Fix MSVC runtime archiver to grab default runtime (#4478)
fbdc810887 Upgrade to C++20 (#4239)
396143004c [ntcore] Add ntcoreffi binary (#4471)
1f45732700 [build] Update to 2023.2.4 native-utils and new dependencies (#4473)
574cb41c18 [ntcore] Various fixes (#4469)
d9d6c425e7 [build] Force Java 11 source compatibility (#4472)
58b6484dbe Switch away from NI interrupt manager to custom implementation (#3705)
ca43fe2798 [wpimath] Use Units conversions in ComputerVisionUtil docs (NFC) (#4464)
87a64ccedc [hal] Convert DutyCycle Raw output to be a high time measurement (#4466)
89a3d00297 [commands] Add FinallyDo and HandleInterrupt decorators (#4412)
1497665f96 [commands] Add C++ versions of Java-only decorators (#4457)
27b173374e [wpimath] Add minLinearAccel parameter to DifferentialDriveAccelerationLimiter (#4422)
2a13dba8ac [wpilib] TrajectoryUtil: Fix ambiguous documentation (NFC) (#4461)
77301b126c [ntcore] NetworkTables 4 (#3217)
90cfa00115 [build] cmake: Fix libssh include directory order (#4459)
5cf961edb9 [commands] Refactor lambda-based commands to inherit FunctionalCommand (#4451)
b2276e47de [wpimath] Enable continuous angle input for HolonomicDriveController (#4453)
893b46139a [fieldImages] Add utilities to simplify loading of fields (#4456)
60e29627c0 [commands] C++ unique_ptr migration (#4319)
3b81cf6c35 [wpilib] Improve Color.toString (#4450)
5c067d30a0 [wpinet] WebSocket: Add SendFrames() (#4445)
ceaf493811 [wpiutil] MakeJByteArray: Use span<uint8> instead of string_view (#4446)
10e04e2b13 [examples] FrisbeeBot: Fix reference capture (#4449)
726f67c64b [build] Add exeSplitSetup (#4444)
c7b7624c1c [wpiutil] Add MessagePack utility functions (#4448)
d600529ec0 [wpinet] uv::Async: Add UnsafeSend() (#4447)
b53b3526a2 [wpimath] Add CoordinateSystem conversion for Transform3d (#4443)
38bb23eb18 [wpimath] Add scalar multiply and divide operators to all geometry classes (#4438)
3937ff8221 [wpilib] Remove deprecated Controller class (#4440)
abbfe244b5 [wpilib] Improve Color FromHSV (#4439)
4ddb8aa0dd [sim] Provide function that resets all simulation data (#4016)
a791470de7 Clean up Java warning suppressions (#4433)
17f504f548 [hal,wpilib] Fix SPI Mode Setting (#4434)
773198537c [wpiutil] Add wpi::scope_exit (#4432)
5ac658c8f0 [wpiutil] Logger: Conditionalize around WPI_LOG (#4431)
8767e4a941 [wpiutil] DataLog: Fix SetMetadata output (#4430)
8c4af073f4 [wpiutil] Synchronization: shutdown race protection (#4429)
c79f38584a [build] Fix Java integration tests (#4428)
36c08dd97c [build] Fix cmake install of fmtlib (#4426)
69b7b3dd7d [ci] Remove the Windows cmake job (#4425)
738c75fed8 [readme] Fix formatting/linting link (#4423)
4eb1d03fb3 [wpimath] Document C++ LinearFilter exception (#4417)
ba4ec6c967 [build] Fix clang-tidy false positive on Linux (#4406)
97836f0e55 [commands] Fix ProfiledPIDSubsystem setGoal behavior (#4414)
fdfb85f695 [wpimath] Remove Java LQR constructor that takes a controller gain matrix (#4419)
ab1baf4832 [wpimath] Add rotation matrix constructor to Rotation3d (#4413)
9730032866 [wpimath] Document LQR and KalmanFilter exceptions (#4418)
5b656eecf6 [wpimath] Fix HTML5 entity (#4420)
9ae38eaa7c [commands] Add owning overload to ProxyScheduleCommand (#4405)
cb33bd71df [commands] deprecate withInterrupt decorator (#4407)
d9b4e7b8bf [commands] Revert "Change grouping decorator impl to flatten nested group structures (#3335)" (#4402)
0389bf5214 [hal] REVPH: Improve handling of disconnected CAN Bus (#4169)
4267fa08d1 [wpilibc] ADIS IMUs: Fix memory leak (#4170)
65c8fbd452 [wpilib] MotorControllerGroup: Override setVoltage (#4403)
f36162fddc [wpimath] Improve Discretization internal docs (#4400)
5149f7d894 [wpimath] Add two-vector Rotation3d constructor (#4398)
20b5bed1cb [wpimath] Clean up Java Quaternion class (#4399)
f18dd1905d [build] Include all thirdparty sources in distribution (#4397)
aa9d7f1cdc [wpiutil] Import foonathan memory (#4306)
2742662254 [ci] Remove a couple of obsolete clang-tidy checks (#4396)
a5df391166 [hal, wpilib] Fix up DIO pulse API (#4387)
59e6706b75 [glass] Turn on docking by default
8461bb1e03 [glass] Add support for saving docking info
b873e208b4 [wpigui] Add support for imgui config flags
873e72df8c [build] Update imgui to 1.88 docking branch
c8bd6fc5b4 [ci] Fix comment-command (take 2) (#4395)
fed68b83b4 [ci] Fix comment-command action not running runners (#4393)
0ef8a4e1df [wpimath] Support formatting more Eigen types (#4391)
c393b3b367 [build] Update to native utils 2023.1.0 and Gradle 7.5.1 (#4392)
b5a17f762c [wpimath] Add direction to slew rate limiter (#4377)
fafc81ed1a [wpiutil] Upgrade to fmt 9.1.0 (#4389)
cc56bdc787 [wpiutil] SafeThread: Add Synchronization object variant (#4382)
4254438d8d [commands] Mark command group lifecycle methods as final (#4385)
97c15af238 [wpimath] LinearSystemId: Fix docs, move C++ impls out of header (#4388)
d22ff8a158 [wpiutil] Add JNI access to C++ stderr (#4381)
fdb5a2791f [wpiutil] jni_util: Add Mac-friendly MakeJLongArray/JArrayRef (#4383)
c3a93fb995 [commands] Revamp Interruptible (#4192)
f2a8d38d2a [commands] Rename Command.repeat to repeatedly (#4379)
9e24c6eac0 [wpiutil] Logger: paren-protect instance usage in macro (#4384)
fe4d12ce22 [wpimath] Add LTV controller derivations and make enums private (#4380)
eb08486039 [build] Fix MacOS binary rpath generation (#4376)
ccf83c634a [build] Use native-utils platform names instead of raw strings (#4375)
3fd69749e7 [docs] Upgrade to doxygen 1.9.4 (#4370)
594df5fc08 [wpinet] uv/util.h: Pull in ws2_32.lib on Windows for ntohs (#4371)
539070820d [ci] Enable asan for wpinet and wpiutil (#4369)
564a56d99b [wpinet] Fix memory leak in WorkerThreadTest (#4368)
5adf50d93c [upstream_utils] Refactor upstream_utils scripts (#4367)
d80e8039d7 [wpiutil] Suppress fmtlib clang-tidy warning in C++20 consteval contexts (#4364)
0e6d67b23b [upstream_utils] Remove yapf format disable comment (#4366)
be5270697a [build] Suppress enum-enum deprecation warning in OpenCV (#4365)
8d28851263 Add Rosetta install command to build requirements (#4363)
3d2115c93e [wpinet] include-what-you-use in MulticastTest (#4360)
91002ae3cc [wpimath] Upgrade to Drake 1.6.0 (#4361)
148c18e658 [wpinet] Upgrade to libuv 1.44.2 (#4362)
a2a5c926b6 Fix clang-tidy warnings (#4359)
ea6b1d8449 [wpiutil] Remove unused ManagedStatic class (#4358)
ac9be78e27 Use stricter C++ type conversions (#4357)
151dabb2af [wpiutil] Upgrade to fmt 9.0.0 (#4337)
340465c929 [ci] Upgrade to clang-format and clang-tidy 14 (NFC) (#4347)
d45bcddd15 [examples] Add comments to StateSpaceDifferentialDrive (#4341)
0e0786331a Update LLVM libraries to 14.0.6 (#4350)
c5db23f296 [wpimath] Add Eigen sparse matrix and iterative solver support (#4349)
44abc8dfa6 [upstream_utils] Remove git version from upstream patches (#4351)
3fdb2f767d [wpimath] Add comments with Ramsete equations (#4348)
0485f05da9 [wpilibjExamples] Upgrade jacoco to match allwpilib (#4346)
0a5eb65231 [wpinet] Handle empty txt block for mdns announcer (#4072)
19ffebaf3e [wpilib] Add reference to I2C Lockup to API Docs (NFC) (#4340)
ce1a90d639 [hal] Replace SerialHelper "goto done" with continue (#4342)
d25af48797 [ci] Make upstream_utils CI fail on untracked files (#4339)
ebb836dacb [examples] Fix negations in event loop examples (#4334)
d83e202f00 [upstream_utils] Update paths in update_fmt.py (#4338)
3ccf806064 [wpimath] Remove redundant LinearFilter.finiteDifference() argument (#4335)
6f1e01f8bd [wpimath] Document example of online filtering for LinearFilter.finiteDifference() (#4336)
1023c34b1c [readme] Update location of ni-libraries (#4333)
faa29d596c [wpilib] Improve Notifier docs (NFC) (#4326)
add00a96ed [wpimath] Improve DifferentialDriveAccelerationLimiter docs (NFC) (#4323)
82fac41244 [wpimath] Better document trackwidth parameters (NFC) (#4324)
5eb44e22a9 Format Python scripts with black (NFC) (#4325)
2e09fa7325 [build] Fix mpack cmake (#4322)
fe3c24b1ee [command] Add ignoringDisable decorator (#4305)
aa221597bc [build] Add M1 builds, change arm name, update to 2023 deps (#4315)
579a8ee229 [ci] Use one worker for Windows release Gradle build (#4318)
5105c5eab6 [wpilibj] Change "final" to "exit" in the IterativeRobotBase JavaDoc (NFC) (#4317)
787fe6e7a5 [wpiutil] Separate third party libraries (#4190)
6671f8d099 [wpigui] Update portable file dialogs (#4316)
9ac9b69aa2 [command] Reorder Scheduler operations (#4261)
e61028cb18 [build] halsim_gui: Add wpinet dependency (#4313)
661d23eaf5 [glass] Add precision setting for NetworkTable view (#4311)
666040e3e5 [hal] Throw exceptions for invalid sizes in I2C and SPI JNI (#4312)
aebc272449 [build] Upgrade to spotbugs Gradle plugin 5.0.8 (#4310)
fd884581e4 [wpilib] Add BooleanEvent/Trigger factories on HID classes (#4247)
9b1bf5c7f1 [wpimath] Move Drake and Eigen to thirdparty folders (#4307)
c9e620a920 [wpilibc] Change EventLoop data structure to vector (#4304)
41d40dd62f [wpinet] Fix libuv unused variable warning on Mac (#4299)
30f5b68264 [wpinet] Fix JNI loading error (#4295)
f7b3f4b90e [examples] Getting Started: Change Joystick to XboxController (#4194)
a99c11c14c [wpimath] Replace UKF implementation with square root form (#4168)
45b7fc445b [wpilib] Add EventLoop (#4104)
16a4888c52 [wpilib] Default off LiveWindow telemetry (#4301)
17752f1337 [ci] Split debug and release Windows builds (#4277)
abb45a68db [commands] Remove custom test wrappers (#4296)
1280a54ef3 [upstream_utils]: Make work with Python 3.8 (#4298)
f2d243fa68 [build] Change defaults for Java lints (#4300)
a4787130f4 Update using development build to work with 2023 gradlerio (#4294)
af7985e46c [wpiutil] Use invoke_result_t instead of result_of in future.h (#4293)
e9d1b5c2d0 [hal] Remove deprecated SimDevice functions (#4209)
45b598d236 [wpilibj] Add toString() methods to Color and Color8Bit (#4286)
fc37265da5 [wpimath] Add angle measurement convention to ArmFeedforward docs (NFC) (#4285)
a4ec13eb0e [wpilibjexamples] Remove unnecessary voltage desaturation
2fa52007af [wpilibc] Use GetBatteryVoltage() in MotorController::SetVoltage
d9f9cd1140 [wpimath] Reset prev_time on pose estimator reset (#4283)
8b6df88783 [wpilibj] Tachometer.getFrequency(): Fix bug (#4281)
345cff08c0 [wpiutil] Make wpi::array constexpr (#4278)
57428112ac [wpimath] Upgrade to Drake v1.3.0 (#4279)
a18d4ff154 [build] Fix tools not being copied when built with -Ponly* (#4276)
d1cd07b9f3 [wpigui] Add OpenURL (#4273)
e67f8e917a [glass] Use glfwSetKeyCallback for Enter key remap (#4275)
be2fedfe50 [wpimath] Add stdexcept include for std::invalid_argument (IWYU) (#4274)
7ad2be172e [build] Update native-utils to 2023.0.1 (#4272)
abc605c9c9 [ci] Update workflows to 20.04 base image (#4271)
3e94805220 [wpiutil] Reduce llvm collections patches (#4268)
db2e1d170e [upstream_utils] Document how to update thirdparty libraries (#4253)
96ebdcaf16 [wpimath] Remove unused Eigen AutoDiff module (#4267)
553b2a3b12 [upstream_utils] Fix stackwalker (#4265)
3e13ef42eb [wpilibc] Add missing std::array #include (include-what-you-use) (#4266)
d651a1fcec Fix internal deprecation warnings (#4257)
b193b318c1 [commands] Add unless() decorator (#4244)
ef3714223b [commands] Remove docs reference to obsolete interrupted() method (NFC) (#4262)
3d8dbbbac3 [readme] Add quickstart (#4225)
013efdde25 [wpinet] Wrap a number of newer libuv features (#4260)
816aa4e465 [wpilib] Add Pneumatics sim classes (#4033)
046c2c8972 [wpilibc] Rename SpeedControllerGroupTest.cpp (#4258)
d80e9cdf64 [upstream_utils] Use shallow clones for thirdparty repos (#4255)
7576136b4a [upstream_utils] Make update_llvm.py executable (#4254)
c3b223ce60 [wpiutil] Vendor llvm and update to 13.0.0 (#4224)
5aa67f56e6 [wpimath] Clean up math comments (#4252)
fff4d1f44e [wpimath] Extend Eigen warning suppression to GCC 12 (#4251)
0d9956273c [wpimath] Add CoordinateSystem.convert() translation and rotation overloads (#4227)
3fada4e0b4 [wpinet] Update to libuv 1.44.1 (#4232)
65b23ac45e [wpilibc] Fix return value of DriverStation::GetJoystickAxisType() (#4230)
4ac34c0141 [upstream_utils] Cleanup update_libuv.py (#4249)
8bd614bb1e [upstream_utils] Use "git am" instead of "git apply" for patches (#4248)
4253d6d5f0 [upstream_utils] Apply "git am" patches individually (#4250)
6a4752dcdc Fix GCC 12.1 warning false positives (#4246)
5876b40f08 [wpimath] Memoize CoordinateSystem and CoordinateAxis statics (#4241)
5983434a70 [cameraserver] Replace IterativeRobot in comment sample code with TimedRobot (#4238)
a3d44a1e69 [wpimath] Add Translation2d.getAngle() (#4217)
d364bbd5a7 [upstream_utils] Give vendor update scripts execute permissions (#4226)
f341e1b2be [wpimath] Document standard coordinate systems better (NFC) (#4228)
9af389b200 [wpinet] AddrToName: Initialize name (#4229)
2ae4adf2d7 [ci] Add wpiformat command to PRs (#4223)
178b2a1e88 Contributing.md: Correct version of clang-format used (#4222)
18db343cdc [wpiutil, wpinet] Vendor libuv, stack walker (#4219)
f0c821282a [build] Use artifactory mirror (#4220)
d673ead481 [wpinet] Move network portions of wpiutil into new wpinet library (#4077)
b33715db15 [wpimath] Add CoordinateSystem class (#4214)
99424ad562 [sim] Allow creating a PWMSim object from a PWMMotorController (#4039)
dc6f641fd2 [wpimath] PIDController: Reset position and velocity error when reset() is called. (#4064)
f20a20f3f1 [wpimath] Add 3D geometry classes (#4175)
708a4bc3bc [wpimath] Conserve previously calculated swerve module angles when updating states for stationary ChassisSpeeds (#4208)
ef7ed21a9d [wpimath] Improve accuracy of ComputerVisionUtil.calculateDistanceToTarget() (#4215)
b1abf455c1 [wpimath] LTVUnicycleController: Use LUT, provide default hyperparameters (#4213)
d5456cf278 [wpimath] LTVDifferentialDriveController: Remove unused variable (#4212)
99343d40ba [command] Remove old command-based framework (#4211)
ee03a7ad3b Remove most 2022 deprecations (#4205)
ce1a7d698a [wpimath] Refactor WheelVoltages inner class to a separate file (#4203)
87bf70fa8e [wpimath] Add LTV controllers (#4094)
ebd2a303bf [wpimath] Remove deprecated MakeMatrix() function (#4202)
e28776d361 [wpimath] LinearSystemLoop: Add extern templates for common cases
dac1429aa9 [wpimath] LQR: Use extern template instead of Impl class
e767605e94 [wpimath] Add typedefs for common types
97c493241f [wpimath] UnscentedKalmanFilter: Move implementation out-of-line
8ea90d8bc9 [wpimath] ExtendedKalmanFilter: Move implementation out-of-line
ae7b1851ec [wpimath] KalmanFilter: Use extern template instead of Impl class
e3d62c22d3 [wpimath] Add extern templates for common cases
7200c4951d [wpiutil] SymbolExports: Add WPILIB_IMPORTS for dllimport
84056c9347 [wpiutil] SymbolExports: Add EXPORT_TEMPLATE_DECLARE/DEFINE
09cf6eeecb [wpimath] ApplyDeadband: add a scale param (#3865)
03230fc842 [build,ci] Enable artifactory build cache (#4200)
63cf3aaa3f [examples] Don't square ArcadeDrive inputs in auto (#4201)
18ff694f02 [wpimath] Add Rotation2d.fromRadians factory (#4178)
4f79ceedd9 [wpilibc] Add missing #include (#4198)
f7ca72fb41 [command] Rename PerpetualCommand to EndlessCommand (#4177)
a06b3f0307 [hal] Correct documentation on updateNotifierAlarm (#4156)
d926dd1610 [wpimath] Fix pose estimator performance (#4111)
51bc893bc5 [wpiutil] CircularBuffer: Change Java package-private methods to public (#4181)
fbe761f7f6 [build] Increase Gradle JVM heap size (#4172)
5ebe911933 [wpimath] Add DifferentialDriveAccelerationLimiter (#4091)
3919250da2 [wpilibj] Remove finalizers (#4158)
b3aee28388 [commands] Allow BooleanSupplier for Trigger operations (#4103)
9d20ab3024 [wpilib] Allow disabling ElevatorSim gravity (#4145)
aaa69f6717 [ci] Remove 32-bit Windows builds (#4078)
355a11a414 Update Java linters and fix new PMD errors (#4157)
ffc69d406c [examples] Reduce suggested acceleration in Ramsete example (#4171)
922d50079a [wpimath] Units: fix comment in degreesToRotations (NFC) (#4159)
dd163b62ae [wpimath] Rotation2d: Add factory method that uses rotations (#4166)
bd80e220b9 [ci] Upgrade CMake actions (#4161)
aef4b16d4c [wpimath] Remove unnecessary NOLINT in LinearPlantInversionFeedforward (NFC) (#4155)
975171609e [wpilib] Compressor: Rename enabled to isEnabled (#4147)
5bf46a9093 [wpimath] Add ComputerVisionUtil (#4124)
f27a1f9bfb [commands] Fix JoystickButton.getAsBoolean (#4131)
1b26e2d5da [commands] Add RepeatCommand (#4009)
88222daa3d [hal] Fix misspelling in AnalogInput/Output docs (NFC) (#4153)
81c5b41ce1 [wpilibj] Document MechanismLigament2d angle unit (NFC) (#4142)
9650e6733e [wpiutil] DataLog: Document finish and thread safety (NFC) (#4140)
c8905ec29a [wpimath] Remove ImplicitModelFollower dt argument (#4119)
b4620f01f9 [wpimath] Fix Rotation2d interpolation in Java (#4125)
2e462a19d3 [wpimath] Constexprify units unary operators (#4138)
069f932e59 [build] Fix gl3w cmake build (#4139)
126e3de91a [wpilibc] Remove unused SetPriority() call from Ultrasonic (#4123)
ba0dccaae4 [wpimath] Fix reference to Rotation2d.fromRadians() (#4118)
e1b6e5f212 [wpilib] Improve MotorSafety documentation (NFC) (#4120)
8d79dc8738 [wpimath] Add ImplicitModelFollower (#4056)
78108c2aba [wpimath] Fix PIDController having incorrect error after calling SetSetpoint() (#4070)
cdafc723fb [examples] Remove unused LinearPlantInversionFeedforward includes (#4069)
0d70884dce [wpimath] Add InterpolatedTreeMap (#4073)
765efa325e [wpimath] Remove redundant column index from vectors (#4116)
89ffcbbe41 [wpimath] Update TrapezoidProfile class name in comment (NFC) (#4107)
95ae23b0e7 [wpimath] Improve EKF numerical stability (#4093)
d5cb6fed67 [wpimath] Support zero cost entries in MakeCostMatrix() (#4100)
d0fef18378 [wpimath] Remove redundant `this.` from ExtendedKalmanFilter.java (#4115)
d640c0f41f [wpimath] Fix pose estimator local measurement standard deviation docs (NFC) (#4113)
a2fa5e3ff7 [wpilibc] BatterySim: Provide non-initializer list versions of Calculate (#4076)
a3eea9958e [hal] Add link to FRC CAN Spec (NFC) (#4086)
db27331d7b [wpilib] Update DifferentialDrive docs (NFC) (#4085)
fdfb31f164 [dlt] Export boolean[] values (#4082)
f93c3331b3 [wpigui] disable changing directory when initializing on MacOS (#4092)
ab7ac4fbb9 [build] Fix various warnings in cmake builds (#4081)
bc39a1a293 [wpilibc] Fix moved pneumatics objects not destructing properly (#4068)
2668130e70 [wpimath] Remove SwerveDrivePoseEstimator encoder reset warning (#4066)
d27ed3722b [ci] Set actions workflow concurrency (#4060)
dae18308c9 [wpimath] Minor fixes to Rotation2d docs (NFC) (#4055)
d66555e42f [datalogtool] Add datalogtool
9f52d8a3b1 [wpilib] DriverStation: Add DataLog support for modes and joystick data
757ea91932 [wpilib] Add DataLogManager
02a804f1c5 [ntcore] Add DataLog support
9b500df0d9 [wpiutil] Add high speed data logging
5a89575b3a [wpiutil] Import customized LLVM MemoryBuffer
b8c4d7527b [wpiutil] Add MappedFileRegion
ac5d46cfa7 [wpilibc] Fix ProfiledPID SetTolerance default velocity value (#4054)
bc9e96e86f [wpilib] Absolute Encoder API and behavior fixes (#4052)
f88c435dd0 [hal] Add mechanism to cancel all periodic callbacks (#4049)

Change-Id: I49aa5b08abbefc7a045e99e19d48ce2cd8fc4d1b
git-subtree-dir: third_party/allwpilib
git-subtree-split: 83f1860047c86aa3330fcb41caf3b2047e074804
Signed-off-by: James Kuszmaul <jabukuszmaul+collab@gmail.com>
diff --git a/wpinet/src/dev/java/edu/wpi/first/net/DevMain.java b/wpinet/src/dev/java/edu/wpi/first/net/DevMain.java
new file mode 100644
index 0000000..7c71a45
--- /dev/null
+++ b/wpinet/src/dev/java/edu/wpi/first/net/DevMain.java
@@ -0,0 +1,17 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+package edu.wpi.first.net;
+
+import edu.wpi.first.util.RuntimeDetector;
+
+public final class DevMain {
+  /** Main entry point. */
+  public static void main(String[] args) {
+    System.out.println("Hello World!");
+    System.out.println(RuntimeDetector.getPlatformPath());
+  }
+
+  private DevMain() {}
+}
diff --git a/wpinet/src/dev/native/cpp/main.cpp b/wpinet/src/dev/native/cpp/main.cpp
new file mode 100644
index 0000000..d0e2f92
--- /dev/null
+++ b/wpinet/src/dev/native/cpp/main.cpp
@@ -0,0 +1,11 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#include <fmt/core.h>
+#include <wpi/SmallString.h>
+
+int main() {
+  wpi::SmallString<128> v1("Hello");
+  fmt::print("{}\n", v1.str());
+}
diff --git a/wpinet/src/main/java/edu/wpi/first/net/MulticastServiceAnnouncer.java b/wpinet/src/main/java/edu/wpi/first/net/MulticastServiceAnnouncer.java
new file mode 100644
index 0000000..6dff454
--- /dev/null
+++ b/wpinet/src/main/java/edu/wpi/first/net/MulticastServiceAnnouncer.java
@@ -0,0 +1,57 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+package edu.wpi.first.net;
+
+import java.util.Map;
+
+/** Class to announce over mDNS that a service is available. */
+public class MulticastServiceAnnouncer implements AutoCloseable {
+  private final int m_handle;
+
+  /**
+   * Creates a MulticastServiceAnnouncer.
+   *
+   * @param serviceName service name
+   * @param serviceType service type
+   * @param port port
+   * @param txt txt
+   */
+  public MulticastServiceAnnouncer(
+      String serviceName, String serviceType, int port, Map<String, String> txt) {
+    String[] keys = txt.keySet().toArray(String[]::new);
+    String[] values = txt.values().toArray(String[]::new);
+    m_handle =
+        WPINetJNI.createMulticastServiceAnnouncer(serviceName, serviceType, port, keys, values);
+  }
+
+  /**
+   * Creates a MulticastServiceAnnouncer.
+   *
+   * @param serviceName service name
+   * @param serviceType service type
+   * @param port port
+   */
+  public MulticastServiceAnnouncer(String serviceName, String serviceType, int port) {
+    m_handle =
+        WPINetJNI.createMulticastServiceAnnouncer(serviceName, serviceType, port, null, null);
+  }
+
+  @Override
+  public void close() {
+    WPINetJNI.freeMulticastServiceAnnouncer(m_handle);
+  }
+
+  public void start() {
+    WPINetJNI.startMulticastServiceAnnouncer(m_handle);
+  }
+
+  public void stop() {
+    WPINetJNI.stopMulticastServiceAnnouncer(m_handle);
+  }
+
+  public boolean hasImplementation() {
+    return WPINetJNI.getMulticastServiceAnnouncerHasImplementation(m_handle);
+  }
+}
diff --git a/wpinet/src/main/java/edu/wpi/first/net/MulticastServiceResolver.java b/wpinet/src/main/java/edu/wpi/first/net/MulticastServiceResolver.java
new file mode 100644
index 0000000..2426de2
--- /dev/null
+++ b/wpinet/src/main/java/edu/wpi/first/net/MulticastServiceResolver.java
@@ -0,0 +1,44 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+package edu.wpi.first.net;
+
+/** Class to resolve a service over mDNS. */
+public class MulticastServiceResolver implements AutoCloseable {
+  private final int m_handle;
+
+  /**
+   * Creates a MulticastServiceResolver.
+   *
+   * @param serviceType service type to look for
+   */
+  public MulticastServiceResolver(String serviceType) {
+    m_handle = WPINetJNI.createMulticastServiceResolver(serviceType);
+  }
+
+  @Override
+  public void close() {
+    WPINetJNI.freeMulticastServiceResolver(m_handle);
+  }
+
+  public void start() {
+    WPINetJNI.startMulticastServiceResolver(m_handle);
+  }
+
+  public void stop() {
+    WPINetJNI.stopMulticastServiceResolver(m_handle);
+  }
+
+  public boolean hasImplementation() {
+    return WPINetJNI.getMulticastServiceResolverHasImplementation(m_handle);
+  }
+
+  public int getEventHandle() {
+    return WPINetJNI.getMulticastServiceResolverEventHandle(m_handle);
+  }
+
+  public ServiceData[] getData() {
+    return WPINetJNI.getMulticastServiceResolverData(m_handle);
+  }
+}
diff --git a/wpinet/src/main/java/edu/wpi/first/net/PortForwarder.java b/wpinet/src/main/java/edu/wpi/first/net/PortForwarder.java
new file mode 100644
index 0000000..904e4f0
--- /dev/null
+++ b/wpinet/src/main/java/edu/wpi/first/net/PortForwarder.java
@@ -0,0 +1,36 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+package edu.wpi.first.net;
+
+/**
+ * Forward ports to another host. This is primarily useful for accessing Ethernet-connected devices
+ * from a computer tethered to the RoboRIO USB port.
+ */
+public final class PortForwarder {
+  private PortForwarder() {
+    throw new UnsupportedOperationException("This is a utility class!");
+  }
+
+  /**
+   * Forward a local TCP port to a remote host and port. Note that local ports less than 1024 won't
+   * work as a normal user.
+   *
+   * @param port local port number
+   * @param remoteHost remote IP address / DNS name
+   * @param remotePort remote port number
+   */
+  public static void add(int port, String remoteHost, int remotePort) {
+    WPINetJNI.addPortForwarder(port, remoteHost, remotePort);
+  }
+
+  /**
+   * Stop TCP forwarding on a port.
+   *
+   * @param port local port number
+   */
+  public static void remove(int port) {
+    WPINetJNI.removePortForwarder(port);
+  }
+}
diff --git a/wpinet/src/main/java/edu/wpi/first/net/ServiceData.java b/wpinet/src/main/java/edu/wpi/first/net/ServiceData.java
new file mode 100644
index 0000000..126526b
--- /dev/null
+++ b/wpinet/src/main/java/edu/wpi/first/net/ServiceData.java
@@ -0,0 +1,66 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+package edu.wpi.first.net;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/** Service data for MulticastServiceResolver. */
+public class ServiceData {
+  /**
+   * Constructs a ServiceData.
+   *
+   * @param ipv4Address ipv4Address
+   * @param port port
+   * @param serviceName found service name
+   * @param hostName found host name
+   * @param keys txt keys
+   * @param values txt values
+   */
+  public ServiceData(
+      long ipv4Address,
+      int port,
+      String serviceName,
+      String hostName,
+      String[] keys,
+      String[] values) {
+    this.m_serviceName = serviceName;
+    this.m_hostName = hostName;
+    this.m_port = port;
+    this.m_ipv4Address = ipv4Address;
+
+    m_txt = new HashMap<>();
+
+    for (int i = 0; i < keys.length; i++) {
+      m_txt.put(keys[i], values[i]);
+    }
+  }
+
+  public Map<String, String> getTxt() {
+    return m_txt;
+  }
+
+  public String getHostName() {
+    return m_hostName;
+  }
+
+  public String getServiceName() {
+    return m_serviceName;
+  }
+
+  public int getPort() {
+    return m_port;
+  }
+
+  public long getIpv4Address() {
+    return m_ipv4Address;
+  }
+
+  private final Map<String, String> m_txt;
+  private final long m_ipv4Address;
+  private final int m_port;
+  private final String m_serviceName;
+  private final String m_hostName;
+}
diff --git a/wpinet/src/main/java/edu/wpi/first/net/WPINetJNI.java b/wpinet/src/main/java/edu/wpi/first/net/WPINetJNI.java
new file mode 100644
index 0000000..d0bcf7d
--- /dev/null
+++ b/wpinet/src/main/java/edu/wpi/first/net/WPINetJNI.java
@@ -0,0 +1,85 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+package edu.wpi.first.net;
+
+import edu.wpi.first.util.RuntimeLoader;
+import java.io.IOException;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+public class WPINetJNI {
+  static boolean libraryLoaded = false;
+  static RuntimeLoader<WPINetJNI> loader = null;
+
+  public static class Helper {
+    private static AtomicBoolean extractOnStaticLoad = new AtomicBoolean(true);
+
+    public static boolean getExtractOnStaticLoad() {
+      return extractOnStaticLoad.get();
+    }
+
+    public static void setExtractOnStaticLoad(boolean load) {
+      extractOnStaticLoad.set(load);
+    }
+  }
+
+  static {
+    if (Helper.getExtractOnStaticLoad()) {
+      try {
+        loader =
+            new RuntimeLoader<>(
+                "wpinetjni", RuntimeLoader.getDefaultExtractionRoot(), WPINetJNI.class);
+        loader.loadLibrary();
+      } catch (IOException ex) {
+        ex.printStackTrace();
+        System.exit(1);
+      }
+      libraryLoaded = true;
+    }
+  }
+
+  /**
+   * Force load the library.
+   *
+   * @throws IOException if the library failed to load
+   */
+  public static synchronized void forceLoad() throws IOException {
+    if (libraryLoaded) {
+      return;
+    }
+    loader =
+        new RuntimeLoader<>("wpinetjni", RuntimeLoader.getDefaultExtractionRoot(), WPINetJNI.class);
+    loader.loadLibrary();
+    libraryLoaded = true;
+  }
+
+  public static native void addPortForwarder(int port, String remoteHost, int remotePort);
+
+  public static native void removePortForwarder(int port);
+
+  public static native int createMulticastServiceAnnouncer(
+      String serviceName, String serviceType, int port, String[] keys, String[] values);
+
+  public static native void freeMulticastServiceAnnouncer(int handle);
+
+  public static native void startMulticastServiceAnnouncer(int handle);
+
+  public static native void stopMulticastServiceAnnouncer(int handle);
+
+  public static native boolean getMulticastServiceAnnouncerHasImplementation(int handle);
+
+  public static native int createMulticastServiceResolver(String serviceType);
+
+  public static native void freeMulticastServiceResolver(int handle);
+
+  public static native void startMulticastServiceResolver(int handle);
+
+  public static native void stopMulticastServiceResolver(int handle);
+
+  public static native boolean getMulticastServiceResolverHasImplementation(int handle);
+
+  public static native int getMulticastServiceResolverEventHandle(int handle);
+
+  public static native ServiceData[] getMulticastServiceResolverData(int handle);
+}
diff --git a/wpinet/src/main/native/cpp/DsClient.cpp b/wpinet/src/main/native/cpp/DsClient.cpp
new file mode 100644
index 0000000..86f8f00
--- /dev/null
+++ b/wpinet/src/main/native/cpp/DsClient.cpp
@@ -0,0 +1,107 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#include "wpinet/DsClient.h"
+
+#include <fmt/format.h>
+#include <wpi/Logger.h>
+#include <wpi/StringExtras.h>
+#include <wpi/json.h>
+
+#include "wpinet/uv/Tcp.h"
+#include "wpinet/uv/Timer.h"
+
+using namespace wpi;
+
+static constexpr uv::Timer::Time kReconnectTime{500};
+
+DsClient::DsClient(wpi::uv::Loop& loop, wpi::Logger& logger,
+                   const private_init&)
+    : m_logger{logger},
+      m_tcp{uv::Tcp::Create(loop)},
+      m_timer{uv::Timer::Create(loop)} {
+  m_tcp->end.connect([this] {
+    WPI_DEBUG4(m_logger, "DS connection closed");
+    clearIp();
+    // try to connect again
+    m_tcp->Reuse([this] { m_timer->Start(kReconnectTime); });
+  });
+  m_tcp->data.connect([this](wpi::uv::Buffer buf, size_t len) {
+    HandleIncoming({buf.base, len});
+  });
+  m_timer->timeout.connect([this] { Connect(); });
+  Connect();
+}
+
+DsClient::~DsClient() = default;
+
+void DsClient::Close() {
+  m_tcp->Close();
+  m_timer->Close();
+  clearIp();
+}
+
+void DsClient::Connect() {
+  auto connreq = std::make_shared<uv::TcpConnectReq>();
+  connreq->connected.connect([this] {
+    m_json.clear();
+    m_tcp->StopRead();
+    m_tcp->StartRead();
+  });
+
+  connreq->error = [this](uv::Error err) {
+    WPI_DEBUG4(m_logger, "DS connect failure: {}", err.str());
+    // try to connect again
+    m_tcp->Reuse([this] { m_timer->Start(kReconnectTime); });
+  };
+
+  WPI_DEBUG4(m_logger, "Starting DS connection attempt");
+  m_tcp->Connect("127.0.0.1", 1742, connreq);
+}
+
+void DsClient::HandleIncoming(std::string_view in) {
+  // this is very bare-bones, as there are never nested {} in these messages
+  while (!in.empty()) {
+    // if json is empty, look for the first { (and discard)
+    if (m_json.empty()) {
+      auto start = in.find('{');
+      in = wpi::slice(in, start, std::string_view::npos);
+    }
+
+    // look for the terminating } (and save)
+    auto end = in.find('}');
+    if (end == std::string_view::npos) {
+      m_json.append(in);
+      return;  // nothing left to read
+    }
+
+    // have complete json message
+    ++end;
+    m_json.append(wpi::slice(in, 0, end));
+    in = wpi::slice(in, end, std::string_view::npos);
+    ParseJson();
+    m_json.clear();
+  }
+}
+
+void DsClient::ParseJson() {
+  WPI_DEBUG4(m_logger, "DsClient JSON: {}", m_json);
+  unsigned int ip = 0;
+  try {
+    ip = wpi::json::parse(m_json).at("robotIP").get<unsigned int>();
+  } catch (wpi::json::exception& e) {
+    WPI_INFO(m_logger, "DsClient JSON error: {}", e.what());
+    return;
+  }
+
+  if (ip == 0) {
+    clearIp();
+  } else {
+    // Convert number into dotted quad
+    auto newip = fmt::format("{}.{}.{}.{}", (ip >> 24) & 0xff,
+                             (ip >> 16) & 0xff, (ip >> 8) & 0xff, ip & 0xff);
+    WPI_INFO(m_logger, "DS received server IP: {}", newip);
+    setIp(newip);
+  }
+}
diff --git a/wpinet/src/main/native/cpp/EventLoopRunner.cpp b/wpinet/src/main/native/cpp/EventLoopRunner.cpp
new file mode 100644
index 0000000..7c7e79c
--- /dev/null
+++ b/wpinet/src/main/native/cpp/EventLoopRunner.cpp
@@ -0,0 +1,91 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#include "wpinet/EventLoopRunner.h"
+
+#include <wpi/SmallVector.h>
+#include <wpi/condition_variable.h>
+#include <wpi/mutex.h>
+
+#include "wpinet/uv/AsyncFunction.h"
+#include "wpinet/uv/Loop.h"
+
+using namespace wpi;
+
+class EventLoopRunner::Thread : public SafeThread {
+ public:
+  using UvExecFunc = uv::AsyncFunction<void(LoopFunc)>;
+
+  Thread() : m_loop(uv::Loop::Create()) {
+    // set up async handles
+    if (!m_loop) {
+      return;
+    }
+
+    // run function
+    m_doExec = UvExecFunc::Create(
+        m_loop, [loop = m_loop.get()](auto out, LoopFunc func) {
+          func(*loop);
+          out.set_value();
+        });
+  }
+
+  void Main() override {
+    if (m_loop) {
+      m_loop->Run();
+    }
+  }
+
+  // the loop
+  std::shared_ptr<uv::Loop> m_loop;
+
+  // run function
+  std::weak_ptr<UvExecFunc> m_doExec;
+};
+
+EventLoopRunner::EventLoopRunner() {
+  m_owner.Start();
+}
+
+EventLoopRunner::~EventLoopRunner() {
+  Stop();
+}
+
+void EventLoopRunner::Stop() {
+  ExecAsync([](uv::Loop& loop) {
+    // close all handles; this will (eventually) stop the loop
+    loop.Walk([](uv::Handle& h) {
+      h.SetLoopClosing(true);
+      h.Close();
+    });
+  });
+  m_owner.Join();
+}
+
+void EventLoopRunner::ExecAsync(LoopFunc func) {
+  if (auto thr = m_owner.GetThread()) {
+    if (auto doExec = thr->m_doExec.lock()) {
+      doExec->Call(std::move(func));
+    }
+  }
+}
+
+void EventLoopRunner::ExecSync(LoopFunc func) {
+  wpi::future<void> f;
+  if (auto thr = m_owner.GetThread()) {
+    if (auto doExec = thr->m_doExec.lock()) {
+      f = doExec->Call(std::move(func));
+    }
+  }
+  if (f.valid()) {
+    f.wait();
+  }
+}
+
+std::shared_ptr<uv::Loop> EventLoopRunner::GetLoop() {
+  if (auto thr = m_owner.GetThread()) {
+    return thr->m_loop;
+  }
+  return nullptr;
+}
diff --git a/wpinet/src/main/native/cpp/HttpParser.cpp b/wpinet/src/main/native/cpp/HttpParser.cpp
new file mode 100644
index 0000000..c428da2
--- /dev/null
+++ b/wpinet/src/main/native/cpp/HttpParser.cpp
@@ -0,0 +1,193 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#include "wpinet/HttpParser.h"
+
+using namespace wpi;
+
+uint32_t HttpParser::GetParserVersion() {
+  return static_cast<uint32_t>(http_parser_version());
+}
+
+HttpParser::HttpParser(Type type) {
+  http_parser_init(&m_parser,
+                   static_cast<http_parser_type>(static_cast<int>(type)));
+  m_parser.data = this;
+
+  http_parser_settings_init(&m_settings);
+
+  // Unlike the underlying http_parser library, we don't perform callbacks
+  // (other than body) with partial data; instead we buffer and call the user
+  // callback only when the data is complete.
+
+  // on_message_begin: initialize our state, call user callback
+  m_settings.on_message_begin = [](http_parser* p) -> int {
+    auto& self = *static_cast<HttpParser*>(p->data);
+    self.m_urlBuf.clear();
+    self.m_state = kStart;
+    self.messageBegin();
+    return self.m_aborted;
+  };
+
+  // on_url: collect into buffer
+  m_settings.on_url = [](http_parser* p, const char* at, size_t length) -> int {
+    auto& self = *static_cast<HttpParser*>(p->data);
+    // append to buffer
+    if ((self.m_urlBuf.size() + length) > self.m_maxLength) {
+      return 1;
+    }
+    self.m_urlBuf += std::string_view{at, length};
+    self.m_state = kUrl;
+    return 0;
+  };
+
+  // on_status: collect into buffer, call user URL callback
+  m_settings.on_status = [](http_parser* p, const char* at,
+                            size_t length) -> int {
+    auto& self = *static_cast<HttpParser*>(p->data);
+    // use valueBuf for the status
+    if ((self.m_valueBuf.size() + length) > self.m_maxLength) {
+      return 1;
+    }
+    self.m_valueBuf += std::string_view{at, length};
+    self.m_state = kStatus;
+    return 0;
+  };
+
+  // on_header_field: collect into buffer, call user header/status callback
+  m_settings.on_header_field = [](http_parser* p, const char* at,
+                                  size_t length) -> int {
+    auto& self = *static_cast<HttpParser*>(p->data);
+
+    // once we're in header, we know the URL is complete
+    if (self.m_state == kUrl) {
+      self.url(self.m_urlBuf);
+      if (self.m_aborted) {
+        return 1;
+      }
+    }
+
+    // once we're in header, we know the status is complete
+    if (self.m_state == kStatus) {
+      self.status(self.m_valueBuf);
+      if (self.m_aborted) {
+        return 1;
+      }
+    }
+
+    // if we previously were in value state, that means we finished a header
+    if (self.m_state == kValue) {
+      self.header(self.m_fieldBuf, self.m_valueBuf);
+      if (self.m_aborted) {
+        return 1;
+      }
+    }
+
+    // clear field and value when we enter this state
+    if (self.m_state != kField) {
+      self.m_state = kField;
+      self.m_fieldBuf.clear();
+      self.m_valueBuf.clear();
+    }
+
+    // append data to field buffer
+    if ((self.m_fieldBuf.size() + length) > self.m_maxLength) {
+      return 1;
+    }
+    self.m_fieldBuf += std::string_view{at, length};
+    return 0;
+  };
+
+  // on_header_field: collect into buffer
+  m_settings.on_header_value = [](http_parser* p, const char* at,
+                                  size_t length) -> int {
+    auto& self = *static_cast<HttpParser*>(p->data);
+
+    // if we weren't previously in value state, clear the buffer
+    if (self.m_state != kValue) {
+      self.m_state = kValue;
+      self.m_valueBuf.clear();
+    }
+
+    // append data to value buffer
+    if ((self.m_valueBuf.size() + length) > self.m_maxLength) {
+      return 1;
+    }
+    self.m_valueBuf += std::string_view{at, length};
+    return 0;
+  };
+
+  // on_headers_complete: call user status/header/complete callback
+  m_settings.on_headers_complete = [](http_parser* p) -> int {
+    auto& self = *static_cast<HttpParser*>(p->data);
+
+    // if we previously were in url state, that means we finished the url
+    if (self.m_state == kUrl) {
+      self.url(self.m_urlBuf);
+      if (self.m_aborted) {
+        return 1;
+      }
+    }
+
+    // if we previously were in status state, that means we finished the status
+    if (self.m_state == kStatus) {
+      self.status(self.m_valueBuf);
+      if (self.m_aborted) {
+        return 1;
+      }
+    }
+
+    // if we previously were in value state, that means we finished a header
+    if (self.m_state == kValue) {
+      self.header(self.m_fieldBuf, self.m_valueBuf);
+      if (self.m_aborted) {
+        return 1;
+      }
+    }
+
+    self.headersComplete(self.ShouldKeepAlive());
+    return self.m_aborted;
+  };
+
+  // on_body: call user callback
+  m_settings.on_body = [](http_parser* p, const char* at,
+                          size_t length) -> int {
+    auto& self = *static_cast<HttpParser*>(p->data);
+    self.body(std::string_view{at, length}, self.IsBodyFinal());
+    return self.m_aborted;
+  };
+
+  // on_message_complete: call user callback
+  m_settings.on_message_complete = [](http_parser* p) -> int {
+    auto& self = *static_cast<HttpParser*>(p->data);
+    self.messageComplete(self.ShouldKeepAlive());
+    return self.m_aborted;
+  };
+
+  // on_chunk_header: call user callback
+  m_settings.on_chunk_header = [](http_parser* p) -> int {
+    auto& self = *static_cast<HttpParser*>(p->data);
+    self.chunkHeader(p->content_length);
+    return self.m_aborted;
+  };
+
+  // on_chunk_complete: call user callback
+  m_settings.on_chunk_complete = [](http_parser* p) -> int {
+    auto& self = *static_cast<HttpParser*>(p->data);
+    self.chunkComplete();
+    return self.m_aborted;
+  };
+}
+
+void HttpParser::Reset(Type type) {
+  http_parser_init(&m_parser,
+                   static_cast<http_parser_type>(static_cast<int>(type)));
+  m_parser.data = this;
+  m_maxLength = 1024;
+  m_state = kStart;
+  m_urlBuf.clear();
+  m_fieldBuf.clear();
+  m_valueBuf.clear();
+  m_aborted = false;
+}
diff --git a/wpinet/src/main/native/cpp/HttpServerConnection.cpp b/wpinet/src/main/native/cpp/HttpServerConnection.cpp
new file mode 100644
index 0000000..313ee2b
--- /dev/null
+++ b/wpinet/src/main/native/cpp/HttpServerConnection.cpp
@@ -0,0 +1,179 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#include "wpinet/HttpServerConnection.h"
+
+#include <fmt/format.h>
+#include <wpi/SmallString.h>
+#include <wpi/SmallVector.h>
+#include <wpi/SpanExtras.h>
+#include <wpi/StringExtras.h>
+#include <wpi/fmt/raw_ostream.h>
+
+#include "wpinet/raw_uv_ostream.h"
+
+using namespace wpi;
+
+HttpServerConnection::HttpServerConnection(std::shared_ptr<uv::Stream> stream)
+    : m_stream(*stream) {
+  // process HTTP messages
+  m_messageCompleteConn =
+      m_request.messageComplete.connect_connection([this](bool keepAlive) {
+        m_keepAlive = keepAlive;
+        ProcessRequest();
+      });
+
+  // look for Accept-Encoding headers to determine if gzip is acceptable
+  m_request.messageBegin.connect([this] { m_acceptGzip = false; });
+  m_request.header.connect(
+      [this](std::string_view name, std::string_view value) {
+        if (wpi::equals_lower(name, "accept-encoding") &&
+            wpi::contains(value, "gzip")) {
+          m_acceptGzip = true;
+        }
+      });
+
+  // pass incoming data to HTTP parser
+  m_dataConn =
+      stream->data.connect_connection([this](uv::Buffer& buf, size_t size) {
+        m_request.Execute({buf.base, size});
+        if (m_request.HasError()) {
+          // could not parse; just close the connection
+          m_stream.Close();
+        }
+      });
+
+  // close when remote side closes
+  m_endConn =
+      stream->end.connect_connection([h = stream.get()] { h->Close(); });
+
+  // start reading
+  stream->StartRead();
+}
+
+void HttpServerConnection::BuildCommonHeaders(raw_ostream& os) {
+  os << "Server: WebServer/1.0\r\n"
+        "Cache-Control: no-store, no-cache, must-revalidate, pre-check=0, "
+        "post-check=0, max-age=0\r\n"
+        "Pragma: no-cache\r\n"
+        "Expires: Mon, 3 Jan 2000 12:34:56 GMT\r\n";
+}
+
+void HttpServerConnection::BuildHeader(raw_ostream& os, int code,
+                                       std::string_view codeText,
+                                       std::string_view contentType,
+                                       uint64_t contentLength,
+                                       std::string_view extra) {
+  fmt::print(os, "HTTP/{}.{} {} {}\r\n", m_request.GetMajor(),
+             m_request.GetMinor(), code, codeText);
+  if (contentLength == 0) {
+    m_keepAlive = false;
+  }
+  if (!m_keepAlive) {
+    os << "Connection: close\r\n";
+  }
+  BuildCommonHeaders(os);
+  os << "Content-Type: " << contentType << "\r\n";
+  if (contentLength != 0) {
+    fmt::print(os, "Content-Length: {}\r\n", contentLength);
+  }
+  os << "Access-Control-Allow-Origin: *\r\nAccess-Control-Allow-Methods: *\r\n";
+  if (!extra.empty()) {
+    os << extra;
+  }
+  os << "\r\n";  // header ends with a blank line
+}
+
+void HttpServerConnection::SendData(std::span<const uv::Buffer> bufs,
+                                    bool closeAfter) {
+  m_stream.Write(bufs, [closeAfter, stream = &m_stream](auto bufs, uv::Error) {
+    for (auto&& buf : bufs) {
+      buf.Deallocate();
+    }
+    if (closeAfter) {
+      stream->Close();
+    }
+  });
+}
+
+void HttpServerConnection::SendResponse(int code, std::string_view codeText,
+                                        std::string_view contentType,
+                                        std::string_view content,
+                                        std::string_view extraHeader) {
+  SmallVector<uv::Buffer, 4> toSend;
+  raw_uv_ostream os{toSend, 4096};
+  BuildHeader(os, code, codeText, contentType, content.size(), extraHeader);
+  os << content;
+  // close after write completes if we aren't keeping alive
+  SendData(os.bufs(), !m_keepAlive);
+}
+
+void HttpServerConnection::SendStaticResponse(
+    int code, std::string_view codeText, std::string_view contentType,
+    std::string_view content, bool gzipped, std::string_view extraHeader) {
+  // TODO: handle remote side not accepting gzip (very rare)
+
+  std::string_view contentEncodingHeader;
+  if (gzipped /* && m_acceptGzip*/) {
+    contentEncodingHeader = "Content-Encoding: gzip\r\n";
+  }
+
+  SmallVector<uv::Buffer, 4> bufs;
+  raw_uv_ostream os{bufs, 4096};
+  BuildHeader(os, code, codeText, contentType, content.size(),
+              fmt::format("{}{}", extraHeader, contentEncodingHeader));
+  // can send content without copying
+  bufs.emplace_back(content);
+
+  m_stream.Write(bufs, [closeAfter = !m_keepAlive, stream = &m_stream](
+                           auto bufs, uv::Error) {
+    // don't deallocate the static content
+    for (auto&& buf : wpi::drop_back(bufs)) {
+      buf.Deallocate();
+    }
+    if (closeAfter) {
+      stream->Close();
+    }
+  });
+}
+
+void HttpServerConnection::SendError(int code, std::string_view message) {
+  std::string_view codeText, extra, baseMessage;
+  switch (code) {
+    case 401:
+      codeText = "Unauthorized";
+      extra = "WWW-Authenticate: Basic realm=\"CameraServer\"";
+      baseMessage = "401: Not Authenticated!";
+      break;
+    case 404:
+      codeText = "Not Found";
+      baseMessage = "404: Not Found!";
+      break;
+    case 500:
+      codeText = "Internal Server Error";
+      baseMessage = "500: Internal Server Error!";
+      break;
+    case 400:
+      codeText = "Bad Request";
+      baseMessage = "400: Not Found!";
+      break;
+    case 403:
+      codeText = "Forbidden";
+      baseMessage = "403: Forbidden!";
+      break;
+    case 503:
+      codeText = "Service Unavailable";
+      baseMessage = "503: Service Unavailable";
+      break;
+    default:
+      code = 501;
+      codeText = "Not Implemented";
+      baseMessage = "501: Not Implemented!";
+      break;
+  }
+  SmallString<256> content{baseMessage};
+  content += "\r\n";
+  content += message;
+  SendResponse(code, codeText, "text/plain", content.str(), extra);
+}
diff --git a/wpinet/src/main/native/cpp/HttpUtil.cpp b/wpinet/src/main/native/cpp/HttpUtil.cpp
new file mode 100644
index 0000000..12ff084
--- /dev/null
+++ b/wpinet/src/main/native/cpp/HttpUtil.cpp
@@ -0,0 +1,494 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#include "wpinet/HttpUtil.h"
+
+#include <cctype>
+
+#include <fmt/format.h>
+#include <wpi/Base64.h>
+#include <wpi/StringExtras.h>
+#include <wpi/raw_ostream.h>
+
+#include "wpinet/TCPConnector.h"
+
+namespace wpi {
+
+std::string_view UnescapeURI(std::string_view str, SmallVectorImpl<char>& buf,
+                             bool* error) {
+  buf.clear();
+  for (auto i = str.begin(), end = str.end(); i != end; ++i) {
+    // pass non-escaped characters to output
+    if (*i != '%') {
+      // decode + to space
+      if (*i == '+') {
+        buf.push_back(' ');
+      } else {
+        buf.push_back(*i);
+      }
+      continue;
+    }
+
+    // are there enough characters left?
+    if (i + 2 >= end) {
+      *error = true;
+      return {};
+    }
+
+    // replace %xx with the corresponding character
+    unsigned val1 = hexDigitValue(*++i);
+    if (val1 == -1U) {
+      *error = true;
+      return {};
+    }
+    unsigned val2 = hexDigitValue(*++i);
+    if (val2 == -1U) {
+      *error = true;
+      return {};
+    }
+    buf.push_back((val1 << 4) | val2);
+  }
+
+  *error = false;
+  return {buf.data(), buf.size()};
+}
+
+std::string_view EscapeURI(std::string_view str, SmallVectorImpl<char>& buf,
+                           bool spacePlus) {
+  static const char* const hexLut = "0123456789ABCDEF";
+
+  buf.clear();
+  for (auto i = str.begin(), end = str.end(); i != end; ++i) {
+    // pass unreserved characters to output
+    if (std::isalnum(*i) || *i == '-' || *i == '_' || *i == '.' || *i == '~') {
+      buf.push_back(*i);
+      continue;
+    }
+
+    // encode space to +
+    if (spacePlus && *i == ' ') {
+      buf.push_back('+');
+      continue;
+    }
+
+    // convert others to %xx
+    buf.push_back('%');
+    buf.push_back(hexLut[((*i) >> 4) & 0x0f]);
+    buf.push_back(hexLut[(*i) & 0x0f]);
+  }
+
+  return {buf.data(), buf.size()};
+}
+
+HttpQueryMap::HttpQueryMap(std::string_view query) {
+  SmallVector<std::string_view, 16> queryElems;
+  split(query, queryElems, '&', 100, false);
+  for (auto elem : queryElems) {
+    auto [nameEsc, valueEsc] = split(elem, '=');
+    SmallString<64> nameBuf;
+    bool err = false;
+    auto name = wpi::UnescapeURI(nameEsc, nameBuf, &err);
+    // note: ignores duplicates
+    if (!err) {
+      m_elems.try_emplace(name, valueEsc);
+    }
+  }
+}
+
+std::optional<std::string_view> HttpQueryMap::Get(
+    std::string_view name, wpi::SmallVectorImpl<char>& buf) const {
+  auto it = m_elems.find(name);
+  if (it == m_elems.end()) {
+    return {};
+  }
+  bool err = false;
+  auto val = wpi::UnescapeURI(it->second, buf, &err);
+  if (err) {
+    return {};
+  }
+  return val;
+}
+
+HttpPath::HttpPath(std::string_view path) {
+  // special-case root path to be a single empty element
+  if (path == "/") {
+    m_pathEnds.emplace_back(0);
+    return;
+  }
+  wpi::SmallVector<std::string_view, 16> pathElems;
+  split(path, pathElems, '/', 100, false);
+  for (auto elem : pathElems) {
+    SmallString<64> buf;
+    bool err = false;
+    auto val = wpi::UnescapeURI(elem, buf, &err);
+    if (err) {
+      m_pathEnds.clear();
+      return;
+    }
+    m_pathBuf += val;
+    m_pathEnds.emplace_back(m_pathBuf.size());
+  }
+}
+
+bool HttpPath::startswith(size_t start,
+                          std::span<const std::string_view> match) const {
+  if (m_pathEnds.size() < (start + match.size())) {
+    return false;
+  }
+  bool first = start == 0;
+  auto p = m_pathEnds.begin() + start;
+  for (auto m : match) {
+    auto val = slice(m_pathBuf, first ? 0 : *(p - 1), *p);
+    if (val != m) {
+      return false;
+    }
+    first = false;
+    ++p;
+  }
+  return true;
+}
+
+std::string_view HttpPath::operator[](size_t n) const {
+  return slice(m_pathBuf, n == 0 ? 0 : m_pathEnds[n - 1], m_pathEnds[n]);
+}
+
+bool ParseHttpHeaders(raw_istream& is, SmallVectorImpl<char>* contentType,
+                      SmallVectorImpl<char>* contentLength) {
+  if (contentType) {
+    contentType->clear();
+  }
+  if (contentLength) {
+    contentLength->clear();
+  }
+
+  bool inContentType = false;
+  bool inContentLength = false;
+  SmallString<64> lineBuf;
+  for (;;) {
+    std::string_view line = rtrim(is.getline(lineBuf, 1024));
+    if (is.has_error()) {
+      return false;
+    }
+    if (line.empty()) {
+      return true;  // empty line signals end of headers
+    }
+
+    // header fields start at the beginning of the line
+    if (!std::isspace(line[0])) {
+      inContentType = false;
+      inContentLength = false;
+      std::string_view field;
+      std::tie(field, line) = split(line, ':');
+      field = rtrim(field);
+      if (equals_lower(field, "content-type")) {
+        inContentType = true;
+      } else if (equals_lower(field, "content-length")) {
+        inContentLength = true;
+      } else {
+        continue;  // ignore other fields
+      }
+    }
+
+    // collapse whitespace
+    line = ltrim(line);
+
+    // save field data
+    if (inContentType && contentType) {
+      contentType->append(line.begin(), line.end());
+    } else if (inContentLength && contentLength) {
+      contentLength->append(line.begin(), line.end());
+    }
+  }
+}
+
+bool FindMultipartBoundary(raw_istream& is, std::string_view boundary,
+                           std::string* saveBuf) {
+  SmallString<64> searchBuf;
+  searchBuf.resize(boundary.size() + 2);
+  size_t searchPos = 0;
+
+  // Per the spec, the --boundary should be preceded by \r\n, so do a first
+  // pass of 1-byte reads to throw those away (common case) and keep the
+  // last non-\r\n character in searchBuf.
+  if (!saveBuf) {
+    do {
+      is.read(searchBuf.data(), 1);
+      if (is.has_error()) {
+        return false;
+      }
+    } while (searchBuf[0] == '\r' || searchBuf[0] == '\n');
+    searchPos = 1;
+  }
+
+  // Look for --boundary.  Read boundarysize+2 bytes at a time
+  // during the search to speed up the reads, then fast-scan for -,
+  // and only then match the entire boundary.  This will be slow if
+  // there's a bunch of continuous -'s in the output, but that's unlikely.
+  for (;;) {
+    is.read(searchBuf.data() + searchPos, searchBuf.size() - searchPos);
+    if (is.has_error()) {
+      return false;
+    }
+
+    // Did we find the boundary?
+    if (searchBuf[0] == '-' && searchBuf[1] == '-' &&
+        wpi::substr(searchBuf, 2) == boundary) {
+      return true;
+    }
+
+    // Fast-scan for '-'
+    size_t pos = searchBuf.find('-', searchBuf[0] == '-' ? 1 : 0);
+    if (pos == std::string_view::npos) {
+      if (saveBuf) {
+        saveBuf->append(searchBuf.data(), searchBuf.size());
+      }
+    } else {
+      if (saveBuf) {
+        saveBuf->append(searchBuf.data(), pos);
+      }
+
+      // move '-' and following to start of buffer (next read will fill)
+      std::memmove(searchBuf.data(), searchBuf.data() + pos,
+                   searchBuf.size() - pos);
+      searchPos = searchBuf.size() - pos;
+    }
+  }
+}
+
+HttpLocation::HttpLocation(std::string_view url_, bool* error,
+                           std::string* errorMsg)
+    : url{url_} {
+  // Split apart into components
+  std::string_view query{url};
+
+  // scheme:
+  std::string_view scheme;
+  std::tie(scheme, query) = split(query, ':');
+  if (!equals_lower(scheme, "http")) {
+    *errorMsg = "only supports http URLs";
+    *error = true;
+    return;
+  }
+
+  // "//"
+  if (!starts_with(query, "//")) {
+    *errorMsg = "expected http://...";
+    *error = true;
+    return;
+  }
+  query.remove_prefix(2);
+
+  // user:password@host:port/
+  std::string_view authority;
+  std::tie(authority, query) = split(query, '/');
+
+  auto [userpass, hostport] = split(authority, '@');
+  // split leaves the RHS empty if the split char isn't present...
+  if (hostport.empty()) {
+    hostport = userpass;
+    userpass = {};
+  }
+
+  if (!userpass.empty()) {
+    auto [rawUser, rawPassword] = split(userpass, ':');
+    SmallString<64> userBuf, passBuf;
+    user = UnescapeURI(rawUser, userBuf, error);
+    if (*error) {
+      *errorMsg = fmt::format("could not unescape user \"{}\"", rawUser);
+      return;
+    }
+    password = UnescapeURI(rawPassword, passBuf, error);
+    if (*error) {
+      *errorMsg =
+          fmt::format("could not unescape password \"{}\"", rawPassword);
+      return;
+    }
+  }
+
+  std::string_view portStr;
+  std::tie(host, portStr) = rsplit(hostport, ':');
+  if (host.empty()) {
+    *errorMsg = "host is empty";
+    *error = true;
+    return;
+  }
+  if (portStr.empty()) {
+    port = 80;
+  } else if (auto p = parse_integer<int>(portStr, 10)) {
+    port = p.value();
+  } else {
+    *errorMsg = fmt::format("port \"{}\" is not an integer", portStr);
+    *error = true;
+    return;
+  }
+
+  // path?query#fragment
+  std::tie(query, fragment) = split(query, '#');
+  std::tie(path, query) = split(query, '?');
+
+  // Split query string into parameters
+  while (!query.empty()) {
+    // split out next param and value
+    std::string_view rawParam, rawValue;
+    std::tie(rawParam, query) = split(query, '&');
+    if (rawParam.empty()) {
+      continue;  // ignore "&&"
+    }
+    std::tie(rawParam, rawValue) = split(rawParam, '=');
+
+    // unescape param
+    *error = false;
+    SmallString<64> paramBuf;
+    std::string_view param = UnescapeURI(rawParam, paramBuf, error);
+    if (*error) {
+      *errorMsg = fmt::format("could not unescape parameter \"{}\"", rawParam);
+      return;
+    }
+
+    // unescape value
+    SmallString<64> valueBuf;
+    std::string_view value = UnescapeURI(rawValue, valueBuf, error);
+    if (*error) {
+      *errorMsg = fmt::format("could not unescape value \"{}\"", rawValue);
+      return;
+    }
+
+    params.emplace_back(std::make_pair(param, value));
+  }
+
+  *error = false;
+}
+
+void HttpRequest::SetAuth(const HttpLocation& loc) {
+  if (!loc.user.empty()) {
+    SmallString<64> userpass;
+    userpass += loc.user;
+    userpass += ':';
+    userpass += loc.password;
+    Base64Encode(userpass.str(), &auth);
+  }
+}
+
+bool HttpConnection::Handshake(const HttpRequest& request,
+                               std::string* warnMsg) {
+  // send GET request
+  os << "GET /" << request.path << " HTTP/1.1\r\n";
+  os << "Host: " << request.host << "\r\n";
+  if (!request.auth.empty()) {
+    os << "Authorization: Basic " << request.auth << "\r\n";
+  }
+  os << "\r\n";
+  os.flush();
+
+  // read first line of response
+  SmallString<64> lineBuf;
+  std::string_view line = rtrim(is.getline(lineBuf, 1024));
+  if (is.has_error()) {
+    *warnMsg = "disconnected before response";
+    return false;
+  }
+
+  // see if we got a HTTP 200 response
+  std::string_view httpver, code, codeText;
+  std::tie(httpver, line) = split(line, ' ');
+  std::tie(code, codeText) = split(line, ' ');
+  if (!starts_with(httpver, "HTTP")) {
+    *warnMsg = "did not receive HTTP response";
+    return false;
+  }
+  if (code != "200") {
+    *warnMsg = fmt::format("received {} {} response", code, codeText);
+    return false;
+  }
+
+  // Parse headers
+  if (!ParseHttpHeaders(is, &contentType, &contentLength)) {
+    *warnMsg = "disconnected during headers";
+    return false;
+  }
+
+  return true;
+}
+
+void HttpMultipartScanner::SetBoundary(std::string_view boundary) {
+  m_boundaryWith = "\n--";
+  m_boundaryWith += boundary;
+  m_boundaryWithout = "\n";
+  m_boundaryWithout += boundary;
+  m_dashes = kUnknown;
+}
+
+void HttpMultipartScanner::Reset(bool saveSkipped) {
+  m_saveSkipped = saveSkipped;
+  m_state = kBoundary;
+  m_posWith = 0;
+  m_posWithout = 0;
+  m_buf.resize(0);
+}
+
+std::string_view HttpMultipartScanner::Execute(std::string_view in) {
+  if (m_state == kDone) {
+    Reset(m_saveSkipped);
+  }
+  if (m_saveSkipped) {
+    m_buf += in;
+  }
+
+  size_t pos = 0;
+  if (m_state == kBoundary) {
+    for (char ch : in) {
+      ++pos;
+      if (m_dashes != kWithout) {
+        if (ch == m_boundaryWith[m_posWith]) {
+          ++m_posWith;
+          if (m_posWith == m_boundaryWith.size()) {
+            // Found the boundary; transition to padding
+            m_state = kPadding;
+            m_dashes = kWith;  // no longer accept plain 'boundary'
+            break;
+          }
+        } else if (ch == m_boundaryWith[0]) {
+          m_posWith = 1;
+        } else {
+          m_posWith = 0;
+        }
+      }
+
+      if (m_dashes != kWith) {
+        if (ch == m_boundaryWithout[m_posWithout]) {
+          ++m_posWithout;
+          if (m_posWithout == m_boundaryWithout.size()) {
+            // Found the boundary; transition to padding
+            m_state = kPadding;
+            m_dashes = kWithout;  // no longer accept '--boundary'
+            break;
+          }
+        } else if (ch == m_boundaryWithout[0]) {
+          m_posWithout = 1;
+        } else {
+          m_posWithout = 0;
+        }
+      }
+    }
+  }
+
+  if (m_state == kPadding) {
+    for (char ch : drop_front(in, pos)) {
+      ++pos;
+      if (ch == '\n') {
+        // Found the LF; return remaining input buffer (following it)
+        m_state = kDone;
+        if (m_saveSkipped) {
+          m_buf.resize(m_buf.size() - in.size() + pos);
+        }
+        return drop_front(in, pos);
+      }
+    }
+  }
+
+  // We consumed the entire input
+  return {};
+}
+
+}  // namespace wpi
diff --git a/wpinet/src/main/native/cpp/MimeTypes.cpp b/wpinet/src/main/native/cpp/MimeTypes.cpp
new file mode 100644
index 0000000..d5f6fb3
--- /dev/null
+++ b/wpinet/src/main/native/cpp/MimeTypes.cpp
@@ -0,0 +1,69 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#include "wpinet/MimeTypes.h"
+
+#include <wpi/StringExtras.h>
+#include <wpi/StringMap.h>
+
+namespace wpi {
+
+// derived partially from
+// https://github.com/DEGoodmanWilson/libmime/blob/stable/0.1.2/mime/mime.cpp
+std::string_view MimeTypeFromPath(std::string_view path) {
+  // https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types
+  static StringMap<const char*> mimeTypes{
+      // text
+      {"css", "text/css"},
+      {"csv", "text/csv"},
+      {"htm", "text/html"},
+      {"html", "text/html"},
+      {"js", "text/javascript"},
+      {"json", "application/json"},
+      {"map", "application/json"},
+      {"md", "text/markdown"},
+      {"txt", "text/plain"},
+      {"xml", "text/xml"},
+
+      // images
+      {"apng", "image/apng"},
+      {"bmp", "image/bmp"},
+      {"gif", "image/gif"},
+      {"cur", "image/x-icon"},
+      {"ico", "image/x-icon"},
+      {"jpg", "image/jpeg"},
+      {"jpeg", "image/jpeg"},
+      {"png", "image/png"},
+      {"svg", "image/svg+xml"},
+      {"tif", "image/tiff"},
+      {"tiff", "image/tiff"},
+      {"webp", "image/webp"},
+
+      // fonts
+      {"otf", "font/otf"},
+      {"ttf", "font/ttf"},
+      {"woff", "font/woff"},
+
+      // misc
+      {"pdf", "application/pdf"},
+      {"zip", "application/zip"},
+  };
+
+  static const char* defaultType = "application/octet-stream";
+
+  auto pos = path.find_last_of("/");
+  if (pos != std::string_view::npos) {
+    path = wpi::substr(path, pos + 1);
+  }
+  auto dot_pos = path.find_last_of(".");
+  if (dot_pos > 0 && dot_pos != std::string_view::npos) {
+    auto type = mimeTypes.find(wpi::substr(path, dot_pos + 1));
+    if (type != mimeTypes.end()) {
+      return type->getValue();
+    }
+  }
+  return defaultType;
+}
+
+}  // namespace wpi
diff --git a/wpinet/src/main/native/cpp/MulticastHandleManager.cpp b/wpinet/src/main/native/cpp/MulticastHandleManager.cpp
new file mode 100644
index 0000000..d249a1c
--- /dev/null
+++ b/wpinet/src/main/native/cpp/MulticastHandleManager.cpp
@@ -0,0 +1,12 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#include "MulticastHandleManager.h"
+
+using namespace wpi;
+
+MulticastHandleManager& wpi::GetMulticastManager() {
+  static MulticastHandleManager manager;
+  return manager;
+}
diff --git a/wpinet/src/main/native/cpp/MulticastHandleManager.h b/wpinet/src/main/native/cpp/MulticastHandleManager.h
new file mode 100644
index 0000000..8c070f7
--- /dev/null
+++ b/wpinet/src/main/native/cpp/MulticastHandleManager.h
@@ -0,0 +1,26 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#pragma once
+
+#include <memory>
+
+#include <wpi/DenseMap.h>
+#include <wpi/UidVector.h>
+
+#include "wpinet/MulticastServiceAnnouncer.h"
+#include "wpinet/MulticastServiceResolver.h"
+
+namespace wpi {
+struct MulticastHandleManager {
+  wpi::mutex mutex;
+  wpi::UidVector<int, 8> handleIds;
+  wpi::DenseMap<size_t, std::unique_ptr<wpi::MulticastServiceResolver>>
+      resolvers;
+  wpi::DenseMap<size_t, std::unique_ptr<wpi::MulticastServiceAnnouncer>>
+      announcers;
+};
+
+MulticastHandleManager& GetMulticastManager();
+}  // namespace wpi
diff --git a/wpinet/src/main/native/cpp/MulticastServiceAnnouncer.cpp b/wpinet/src/main/native/cpp/MulticastServiceAnnouncer.cpp
new file mode 100644
index 0000000..daa7e8d
--- /dev/null
+++ b/wpinet/src/main/native/cpp/MulticastServiceAnnouncer.cpp
@@ -0,0 +1,67 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#include "wpinet/MulticastServiceAnnouncer.h"
+
+#include <wpi/SmallVector.h>
+
+#include "MulticastHandleManager.h"
+
+extern "C" {
+WPI_MulticastServiceAnnouncerHandle WPI_CreateMulticastServiceAnnouncer(
+    const char* serviceName, const char* serviceType, int32_t port,
+    int32_t txtCount, const char** keys, const char** values)
+
+{
+  auto& manager = wpi::GetMulticastManager();
+  std::scoped_lock lock{manager.mutex};
+
+  wpi::SmallVector<std::pair<std::string_view, std::string_view>, 8> txts;
+
+  for (int32_t i = 0; i < txtCount; i++) {
+    txts.emplace_back(
+        std::pair<std::string_view, std::string_view>{keys[i], values[i]});
+  }
+
+  auto announcer = std::make_unique<wpi::MulticastServiceAnnouncer>(
+      serviceName, serviceType, port, txts);
+
+  size_t index = manager.handleIds.emplace_back(3);
+  manager.announcers[index] = std::move(announcer);
+
+  return index;
+}
+
+void WPI_FreeMulticastServiceAnnouncer(
+    WPI_MulticastServiceAnnouncerHandle handle) {
+  auto& manager = wpi::GetMulticastManager();
+  std::scoped_lock lock{manager.mutex};
+  manager.announcers[handle] = nullptr;
+  manager.handleIds.erase(handle);
+}
+
+void WPI_StartMulticastServiceAnnouncer(
+    WPI_MulticastServiceAnnouncerHandle handle) {
+  auto& manager = wpi::GetMulticastManager();
+  std::scoped_lock lock{manager.mutex};
+  auto& announcer = manager.announcers[handle];
+  announcer->Start();
+}
+
+void WPI_StopMulticastServiceAnnouncer(
+    WPI_MulticastServiceAnnouncerHandle handle) {
+  auto& manager = wpi::GetMulticastManager();
+  std::scoped_lock lock{manager.mutex};
+  auto& announcer = manager.announcers[handle];
+  announcer->Stop();
+}
+
+int32_t WPI_GetMulticastServiceAnnouncerHasImplementation(
+    WPI_MulticastServiceAnnouncerHandle handle) {
+  auto& manager = wpi::GetMulticastManager();
+  std::scoped_lock lock{manager.mutex};
+  auto& announcer = manager.announcers[handle];
+  return announcer->HasImplementation();
+}
+}  // extern "C"
diff --git a/wpinet/src/main/native/cpp/MulticastServiceResolver.cpp b/wpinet/src/main/native/cpp/MulticastServiceResolver.cpp
new file mode 100644
index 0000000..c16db2e
--- /dev/null
+++ b/wpinet/src/main/native/cpp/MulticastServiceResolver.cpp
@@ -0,0 +1,149 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#include "wpinet/MulticastServiceResolver.h"
+
+#include <wpi/MemAlloc.h>
+
+#include "MulticastHandleManager.h"
+
+extern "C" {
+WPI_MulticastServiceResolverHandle WPI_CreateMulticastServiceResolver(
+    const char* serviceType)
+
+{
+  auto& manager = wpi::GetMulticastManager();
+  std::scoped_lock lock{manager.mutex};
+
+  auto resolver = std::make_unique<wpi::MulticastServiceResolver>(serviceType);
+
+  size_t index = manager.handleIds.emplace_back(2);
+  manager.resolvers[index] = std::move(resolver);
+
+  return index;
+}
+
+void WPI_FreeMulticastServiceResolver(
+    WPI_MulticastServiceResolverHandle handle) {
+  auto& manager = wpi::GetMulticastManager();
+  std::scoped_lock lock{manager.mutex};
+  manager.resolvers[handle] = nullptr;
+  manager.handleIds.erase(handle);
+}
+
+void WPI_StartMulticastServiceResolver(
+    WPI_MulticastServiceResolverHandle handle) {
+  auto& manager = wpi::GetMulticastManager();
+  std::scoped_lock lock{manager.mutex};
+  auto& resolver = manager.resolvers[handle];
+  resolver->Start();
+}
+
+void WPI_StopMulticastServiceResolver(
+    WPI_MulticastServiceResolverHandle handle) {
+  auto& manager = wpi::GetMulticastManager();
+  std::scoped_lock lock{manager.mutex};
+  auto& resolver = manager.resolvers[handle];
+  resolver->Stop();
+}
+
+int32_t WPI_GetMulticastServiceResolverHasImplementation(
+    WPI_MulticastServiceResolverHandle handle) {
+  auto& manager = wpi::GetMulticastManager();
+  std::scoped_lock lock{manager.mutex};
+  auto& resolver = manager.resolvers[handle];
+  return resolver->HasImplementation();
+}
+
+WPI_EventHandle WPI_GetMulticastServiceResolverEventHandle(
+    WPI_MulticastServiceResolverHandle handle) {
+  auto& manager = wpi::GetMulticastManager();
+  std::scoped_lock lock{manager.mutex};
+  auto& resolver = manager.resolvers[handle];
+  return resolver->GetEventHandle();
+}
+
+WPI_ServiceData* WPI_GetMulticastServiceResolverData(
+    WPI_MulticastServiceResolverHandle handle, int32_t* dataCount) {
+  std::vector<wpi::MulticastServiceResolver::ServiceData> allData;
+  {
+    auto& manager = wpi::GetMulticastManager();
+    std::scoped_lock lock{manager.mutex};
+    auto& resolver = manager.resolvers[handle];
+    allData = resolver->GetData();
+  }
+  if (allData.empty()) {
+    *dataCount = 0;
+    return nullptr;
+  }
+  size_t allocSize = sizeof(WPI_ServiceData) * allData.size();
+
+  for (auto&& data : allData) {
+    // Include space for hostName and serviceType (+ terminators)
+    allocSize += data.hostName.size() + data.serviceName.size() + 2;
+
+    size_t keysTotalLength = 0;
+    size_t valuesTotalLength = 0;
+    // Include space for all keys and values, and pointer array
+    for (auto&& t : data.txt) {
+      allocSize += sizeof(const char*);
+      keysTotalLength += (t.first.size() + 1);
+      allocSize += sizeof(const char*);
+      valuesTotalLength += (t.second.size() + 1);
+    }
+    allocSize += keysTotalLength;
+    allocSize += valuesTotalLength;
+  }
+
+  uint8_t* cDataRaw = reinterpret_cast<uint8_t*>(wpi::safe_malloc(allocSize));
+  if (!cDataRaw) {
+    return nullptr;
+  }
+  WPI_ServiceData* rootArray = reinterpret_cast<WPI_ServiceData*>(cDataRaw);
+  cDataRaw += (sizeof(WPI_ServiceData) + allData.size());
+  WPI_ServiceData* currentData = rootArray;
+
+  for (auto&& data : allData) {
+    currentData->ipv4Address = data.ipv4Address;
+    currentData->port = data.port;
+    currentData->txtCount = data.txt.size();
+
+    std::memcpy(cDataRaw, data.hostName.c_str(), data.hostName.size() + 1);
+    currentData->hostName = reinterpret_cast<const char*>(cDataRaw);
+    cDataRaw += data.hostName.size() + 1;
+
+    std::memcpy(cDataRaw, data.serviceName.c_str(),
+                data.serviceName.size() + 1);
+    currentData->serviceName = reinterpret_cast<const char*>(cDataRaw);
+    cDataRaw += data.serviceName.size() + 1;
+
+    char** valuesPtrArr = reinterpret_cast<char**>(cDataRaw);
+    cDataRaw += (sizeof(char**) * data.txt.size());
+    char** keysPtrArr = reinterpret_cast<char**>(cDataRaw);
+    cDataRaw += (sizeof(char**) * data.txt.size());
+
+    currentData->txtKeys = const_cast<const char**>(keysPtrArr);
+    currentData->txtValues = const_cast<const char**>(valuesPtrArr);
+
+    for (size_t i = 0; i < data.txt.size(); i++) {
+      keysPtrArr[i] = reinterpret_cast<char*>(cDataRaw);
+      std::memcpy(keysPtrArr[i], data.txt[i].first.c_str(),
+                  data.txt[i].first.size() + 1);
+      cDataRaw += (data.txt[i].first.size() + 1);
+
+      valuesPtrArr[i] = reinterpret_cast<char*>(cDataRaw);
+      std::memcpy(valuesPtrArr[i], data.txt[i].second.c_str(),
+                  data.txt[i].second.size() + 1);
+      cDataRaw += (data.txt[i].second.size() + 1);
+    }
+    currentData++;
+  }
+
+  return rootArray;
+}
+
+void WPI_FreeServiceData(WPI_ServiceData* serviceData, int32_t length) {
+  std::free(serviceData);
+}
+}  // extern "C"
diff --git a/wpinet/src/main/native/cpp/ParallelTcpConnector.cpp b/wpinet/src/main/native/cpp/ParallelTcpConnector.cpp
new file mode 100644
index 0000000..317f0a2
--- /dev/null
+++ b/wpinet/src/main/native/cpp/ParallelTcpConnector.cpp
@@ -0,0 +1,177 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#include "wpinet/ParallelTcpConnector.h"
+
+#include <fmt/format.h>
+#include <wpi/Logger.h>
+
+#include "wpinet/uv/GetAddrInfo.h"
+#include "wpinet/uv/Loop.h"
+#include "wpinet/uv/Tcp.h"
+#include "wpinet/uv/Timer.h"
+#include "wpinet/uv/util.h"
+
+using namespace wpi;
+
+ParallelTcpConnector::ParallelTcpConnector(
+    wpi::uv::Loop& loop, wpi::uv::Timer::Time reconnectRate,
+    wpi::Logger& logger, std::function<void(wpi::uv::Tcp& tcp)> connected,
+    const private_init&)
+    : m_loop{loop},
+      m_logger{logger},
+      m_reconnectRate{reconnectRate},
+      m_connected{std::move(connected)},
+      m_reconnectTimer{uv::Timer::Create(loop)} {
+  m_reconnectTimer->timeout.connect([this] {
+    if (!IsConnected()) {
+      WPI_DEBUG1(m_logger, "timed out, reconnecting");
+      Connect();
+    }
+  });
+}
+
+ParallelTcpConnector::~ParallelTcpConnector() = default;
+
+void ParallelTcpConnector::Close() {
+  CancelAll();
+  m_reconnectTimer->Close();
+}
+
+void ParallelTcpConnector::SetServers(
+    std::span<const std::pair<std::string, unsigned int>> servers) {
+  m_servers.assign(servers.begin(), servers.end());
+  if (!IsConnected()) {
+    Connect();
+  }
+}
+
+void ParallelTcpConnector::Disconnected() {
+  if (m_isConnected) {
+    m_isConnected = false;
+    Connect();
+  }
+}
+
+void ParallelTcpConnector::Succeeded(uv::Tcp& tcp) {
+  if (!m_isConnected) {
+    m_isConnected = true;
+    m_reconnectTimer->Stop();
+    CancelAll(&tcp);
+  }
+}
+
+void ParallelTcpConnector::Connect() {
+  if (IsConnected()) {
+    return;
+  }
+
+  CancelAll();
+  m_reconnectTimer->Start(m_reconnectRate);
+
+  WPI_DEBUG3(m_logger, "starting new connection attempts");
+
+  // kick off parallel lookups
+  for (auto&& server : m_servers) {
+    auto req = std::make_shared<uv::GetAddrInfoReq>();
+    m_resolvers.emplace_back(req);
+
+    req->resolved.connect(
+        [this, req = req.get()](const addrinfo& addrinfo) {
+          if (IsConnected()) {
+            return;
+          }
+
+          // kick off parallel connection attempts
+          for (auto ai = &addrinfo; ai; ai = ai->ai_next) {
+            auto tcp = uv::Tcp::Create(m_loop);
+            m_attempts.emplace_back(tcp);
+
+            auto connreq = std::make_shared<uv::TcpConnectReq>();
+            connreq->connected.connect(
+                [this, tcp = tcp.get()] {
+                  if (m_logger.min_level() <= wpi::WPI_LOG_DEBUG4) {
+                    std::string ip;
+                    unsigned int port = 0;
+                    uv::AddrToName(tcp->GetPeer(), &ip, &port);
+                    WPI_DEBUG4(m_logger,
+                               "successful connection ({}) to {} port {}",
+                               static_cast<void*>(tcp), ip, port);
+                  }
+                  if (IsConnected()) {
+                    tcp->Shutdown([tcp] { tcp->Close(); });
+                    return;
+                  }
+                  if (m_connected) {
+                    m_connected(*tcp);
+                  }
+                },
+                shared_from_this());
+
+            connreq->error = [selfWeak = weak_from_this(),
+                              tcp = tcp.get()](uv::Error err) {
+              if (auto self = selfWeak.lock()) {
+                WPI_DEBUG1(self->m_logger, "connect failure ({}): {}",
+                           static_cast<void*>(tcp), err.str());
+              }
+            };
+
+            if (m_logger.min_level() <= wpi::WPI_LOG_DEBUG4) {
+              std::string ip;
+              unsigned int port = 0;
+              uv::AddrToName(*reinterpret_cast<sockaddr_storage*>(ai->ai_addr),
+                             &ip, &port);
+              WPI_DEBUG4(
+                  m_logger,
+                  "Info({}) starting connection attempt ({}) to {} port {}",
+                  static_cast<void*>(req), static_cast<void*>(tcp.get()), ip,
+                  port);
+            }
+            tcp->Connect(*ai->ai_addr, connreq);
+          }
+        },
+        shared_from_this());
+
+    req->error = [req = req.get(), selfWeak = weak_from_this()](uv::Error err) {
+      if (auto self = selfWeak.lock()) {
+        WPI_DEBUG1(self->m_logger, "GetAddrInfo({}) failure: {}",
+                   static_cast<void*>(req), err.str());
+      }
+    };
+
+    WPI_DEBUG4(m_logger, "starting GetAddrInfo({}) for {} port {}",
+               static_cast<void*>(req.get()), server.first, server.second);
+    addrinfo hints;
+    std::memset(&hints, 0, sizeof(hints));
+    hints.ai_family = AF_UNSPEC;
+    hints.ai_socktype = SOCK_STREAM;
+    hints.ai_protocol = IPPROTO_TCP;
+    hints.ai_flags = AI_NUMERICSERV | AI_ADDRCONFIG;
+    uv::GetAddrInfo(m_loop, req, server.first, fmt::format("{}", server.second),
+                    &hints);
+  }
+}
+
+void ParallelTcpConnector::CancelAll(wpi::uv::Tcp* except) {
+  WPI_DEBUG4(m_logger, "canceling previous attempts");
+  for (auto&& resolverWeak : m_resolvers) {
+    if (auto resolver = resolverWeak.lock()) {
+      WPI_DEBUG4(m_logger, "canceling GetAddrInfo({})",
+                 static_cast<void*>(resolver.get()));
+      resolver->Cancel();
+    }
+  }
+  m_resolvers.clear();
+
+  for (auto&& tcpWeak : m_attempts) {
+    if (auto tcp = tcpWeak.lock()) {
+      if (tcp.get() != except) {
+        WPI_DEBUG4(m_logger, "canceling connection attempt ({})",
+                   static_cast<void*>(tcp.get()));
+        tcp->Close();
+      }
+    }
+  }
+  m_attempts.clear();
+}
diff --git a/wpinet/src/main/native/cpp/PortForwarder.cpp b/wpinet/src/main/native/cpp/PortForwarder.cpp
new file mode 100644
index 0000000..257b620
--- /dev/null
+++ b/wpinet/src/main/native/cpp/PortForwarder.cpp
@@ -0,0 +1,158 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#include "wpinet/PortForwarder.h"
+
+#include <fmt/format.h>
+#include <wpi/DenseMap.h>
+
+#include "wpinet/EventLoopRunner.h"
+#include "wpinet/uv/GetAddrInfo.h"
+#include "wpinet/uv/Tcp.h"
+#include "wpinet/uv/Timer.h"
+
+using namespace wpi;
+
+struct PortForwarder::Impl {
+ public:
+  EventLoopRunner runner;
+  DenseMap<unsigned int, std::weak_ptr<uv::Tcp>> servers;
+};
+
+PortForwarder::PortForwarder() : m_impl{new Impl} {}
+
+PortForwarder& PortForwarder::GetInstance() {
+  static PortForwarder instance;
+  return instance;
+}
+
+static void CopyStream(uv::Stream& in, std::weak_ptr<uv::Stream> outWeak) {
+  in.data.connect([&in, outWeak](uv::Buffer& buf, size_t len) {
+    uv::Buffer buf2 = buf.Dup();
+    buf2.len = len;
+    auto out = outWeak.lock();
+    if (!out) {
+      buf2.Deallocate();
+      in.Close();
+      return;
+    }
+    out->Write({buf2}, [](auto bufs, uv::Error) {
+      for (auto buf : bufs) {
+        buf.Deallocate();
+      }
+    });
+  });
+}
+
+void PortForwarder::Add(unsigned int port, std::string_view remoteHost,
+                        unsigned int remotePort) {
+  m_impl->runner.ExecSync([&](uv::Loop& loop) {
+    auto server = uv::Tcp::Create(loop);
+
+    // bind to local port
+    server->Bind("", port);
+
+    // when we get a connection, accept it
+    server->connection.connect([serverPtr = server.get(),
+                                host = std::string{remoteHost}, remotePort] {
+      auto& loop = serverPtr->GetLoopRef();
+      auto client = serverPtr->Accept();
+      if (!client) {
+        return;
+      }
+
+      // close on error
+      client->error.connect(
+          [clientPtr = client.get()](uv::Error err) { clientPtr->Close(); });
+
+      // connected flag
+      auto connected = std::make_shared<bool>(false);
+      client->SetData(connected);
+
+      auto remote = uv::Tcp::Create(loop);
+      remote->error.connect(
+          [remotePtr = remote.get(),
+           clientWeak = std::weak_ptr<uv::Tcp>(client)](uv::Error err) {
+            remotePtr->Close();
+            if (auto client = clientWeak.lock()) {
+              client->Close();
+            }
+          });
+
+      // resolve address
+      uv::GetAddrInfo(
+          loop,
+          [clientWeak = std::weak_ptr<uv::Tcp>(client),
+           remoteWeak = std::weak_ptr<uv::Tcp>(remote)](const addrinfo& addr) {
+            auto remote = remoteWeak.lock();
+            if (!remote) {
+              return;
+            }
+
+            // connect to remote address/port
+            remote->Connect(*addr.ai_addr, [remotePtr = remote.get(),
+                                            remoteWeak, clientWeak] {
+              auto client = clientWeak.lock();
+              if (!client) {
+                remotePtr->Close();
+                return;
+              }
+              *(client->GetData<bool>()) = true;
+
+              // close both when either side closes
+              client->end.connect([clientPtr = client.get(), remoteWeak] {
+                clientPtr->Close();
+                if (auto remote = remoteWeak.lock()) {
+                  remote->Close();
+                }
+              });
+              remotePtr->end.connect([remotePtr, clientWeak] {
+                remotePtr->Close();
+                if (auto client = clientWeak.lock()) {
+                  client->Close();
+                }
+              });
+
+              // copy bidirectionally
+              client->StartRead();
+              remotePtr->StartRead();
+              CopyStream(*client, remoteWeak);
+              CopyStream(*remotePtr, clientWeak);
+            });
+          },
+          host, fmt::to_string(remotePort));
+
+      // time out for connection
+      uv::Timer::SingleShot(loop, uv::Timer::Time{500},
+                            [connectedWeak = std::weak_ptr<bool>(connected),
+                             clientWeak = std::weak_ptr<uv::Tcp>(client),
+                             remoteWeak = std::weak_ptr<uv::Tcp>(remote)] {
+                              if (auto connected = connectedWeak.lock()) {
+                                if (!*connected) {
+                                  if (auto client = clientWeak.lock()) {
+                                    client->Close();
+                                  }
+                                  if (auto remote = remoteWeak.lock()) {
+                                    remote->Close();
+                                  }
+                                }
+                              }
+                            });
+    });
+
+    // start listening for incoming connections
+    server->Listen();
+
+    m_impl->servers[port] = server;
+  });
+}
+
+void PortForwarder::Remove(unsigned int port) {
+  m_impl->runner.ExecSync([&](uv::Loop& loop) {
+    if (auto server = m_impl->servers.lookup(port).lock()) {
+      server->Close();
+      m_impl->servers.erase(port);
+    }
+  });
+}
diff --git a/wpinet/src/main/native/cpp/SocketError.cpp b/wpinet/src/main/native/cpp/SocketError.cpp
new file mode 100644
index 0000000..233c5de
--- /dev/null
+++ b/wpinet/src/main/native/cpp/SocketError.cpp
@@ -0,0 +1,37 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#include "wpinet/SocketError.h"
+
+#ifdef _WIN32
+#include <winsock2.h>
+#else
+#include <cerrno>
+#include <cstring>
+#endif
+
+namespace wpi {
+
+int SocketErrno() {
+#ifdef _WIN32
+  return WSAGetLastError();
+#else
+  return errno;
+#endif
+}
+
+std::string SocketStrerror(int code) {
+#ifdef _WIN32
+  LPSTR errstr = nullptr;
+  FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, 0,
+                code, 0, (LPSTR)&errstr, 0, 0);
+  std::string rv(errstr);
+  LocalFree(errstr);
+  return rv;
+#else
+  return std::strerror(code);
+#endif
+}
+
+}  // namespace wpi
diff --git a/wpinet/src/main/native/cpp/UDPClient.cpp b/wpinet/src/main/native/cpp/UDPClient.cpp
new file mode 100644
index 0000000..5963d60
--- /dev/null
+++ b/wpinet/src/main/native/cpp/UDPClient.cpp
@@ -0,0 +1,249 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#include "wpinet/UDPClient.h"
+
+#ifdef _WIN32
+#include <WinSock2.h>
+#include <Ws2tcpip.h>
+#pragma comment(lib, "Ws2_32.lib")
+#else
+#include <arpa/inet.h>
+#include <fcntl.h>
+#include <netinet/in.h>
+#include <unistd.h>
+#endif
+
+#include <wpi/Logger.h>
+#include <wpi/SmallString.h>
+
+#include "wpinet/SocketError.h"
+
+using namespace wpi;
+
+UDPClient::UDPClient(Logger& logger) : UDPClient("", logger) {}
+
+UDPClient::UDPClient(std::string_view address, Logger& logger)
+    : m_lsd(0), m_port(0), m_address(address), m_logger(logger) {}
+
+UDPClient::UDPClient(UDPClient&& other)
+    : m_lsd(other.m_lsd),
+      m_port(other.m_port),
+      m_address(std::move(other.m_address)),
+      m_logger(other.m_logger) {
+  other.m_lsd = 0;
+  other.m_port = 0;
+}
+
+UDPClient::~UDPClient() {
+  if (m_lsd > 0) {
+    shutdown();
+  }
+}
+
+UDPClient& UDPClient::operator=(UDPClient&& other) {
+  if (this == &other) {
+    return *this;
+  }
+  shutdown();
+  m_logger = other.m_logger;
+  m_lsd = other.m_lsd;
+  m_address = std::move(other.m_address);
+  m_port = other.m_port;
+  other.m_lsd = 0;
+  other.m_port = 0;
+  return *this;
+}
+
+int UDPClient::start() {
+  return start(0);
+}
+
+int UDPClient::start(int port) {
+  if (m_lsd > 0) {
+    return 0;
+  }
+
+#ifdef _WIN32
+  WSAData wsaData;
+  WORD wVersionRequested = MAKEWORD(2, 2);
+  WSAStartup(wVersionRequested, &wsaData);
+#endif
+
+  m_lsd = socket(AF_INET, SOCK_DGRAM, 0);
+
+  if (m_lsd < 0) {
+    WPI_ERROR(m_logger, "could not create socket");
+    return -1;
+  }
+
+  struct sockaddr_in addr;
+  std::memset(&addr, 0, sizeof(addr));
+  addr.sin_family = AF_INET;
+  if (m_address.size() > 0) {
+#ifdef _WIN32
+    SmallString<128> addr_copy(m_address);
+    addr_copy.push_back('\0');
+    int res = InetPton(PF_INET, addr_copy.data(), &(addr.sin_addr));
+#else
+    int res = inet_pton(PF_INET, m_address.c_str(), &(addr.sin_addr));
+#endif
+    if (res != 1) {
+      WPI_ERROR(m_logger, "could not resolve {} address", m_address);
+      return -1;
+    }
+  } else {
+    addr.sin_addr.s_addr = INADDR_ANY;
+  }
+  addr.sin_port = htons(port);
+
+  if (port != 0) {
+#ifdef _WIN32
+    int optval = 1;
+    setsockopt(m_lsd, SOL_SOCKET, SO_EXCLUSIVEADDRUSE,
+               reinterpret_cast<char*>(&optval), sizeof optval);
+#else
+    int optval = 1;
+    setsockopt(m_lsd, SOL_SOCKET, SO_REUSEADDR,
+               reinterpret_cast<char*>(&optval), sizeof optval);
+#endif
+  }
+
+  int result = bind(m_lsd, reinterpret_cast<sockaddr*>(&addr), sizeof(addr));
+  if (result != 0) {
+    WPI_ERROR(m_logger, "bind() failed: {}", SocketStrerror());
+    return result;
+  }
+  m_port = port;
+  return 0;
+}
+
+void UDPClient::shutdown() {
+  if (m_lsd > 0) {
+#ifdef _WIN32
+    ::shutdown(m_lsd, SD_BOTH);
+    closesocket(m_lsd);
+    WSACleanup();
+#else
+    ::shutdown(m_lsd, SHUT_RDWR);
+    close(m_lsd);
+#endif
+    m_lsd = 0;
+    m_port = 0;
+  }
+}
+
+int UDPClient::send(std::span<const uint8_t> data, std::string_view server,
+                    int port) {
+  // server must be a resolvable IP address
+  struct sockaddr_in addr;
+  std::memset(&addr, 0, sizeof(addr));
+  addr.sin_family = AF_INET;
+  SmallString<128> remoteAddr{server};
+  if (remoteAddr.empty()) {
+    WPI_ERROR(m_logger, "server must be passed");
+    return -1;
+  }
+
+#ifdef _WIN32
+  int res = InetPton(AF_INET, remoteAddr.c_str(), &(addr.sin_addr));
+#else
+  int res = inet_pton(AF_INET, remoteAddr.c_str(), &(addr.sin_addr));
+#endif
+  if (res != 1) {
+    WPI_ERROR(m_logger, "could not resolve {} address", server);
+    return -1;
+  }
+  addr.sin_port = htons(port);
+
+  // sendto should not block
+  int result =
+      sendto(m_lsd, reinterpret_cast<const char*>(data.data()), data.size(), 0,
+             reinterpret_cast<sockaddr*>(&addr), sizeof(addr));
+  return result;
+}
+
+int UDPClient::send(std::string_view data, std::string_view server, int port) {
+  // server must be a resolvable IP address
+  struct sockaddr_in addr;
+  std::memset(&addr, 0, sizeof(addr));
+  addr.sin_family = AF_INET;
+  SmallString<128> remoteAddr{server};
+  if (remoteAddr.empty()) {
+    WPI_ERROR(m_logger, "server must be passed");
+    return -1;
+  }
+
+#ifdef _WIN32
+  int res = InetPton(AF_INET, remoteAddr.c_str(), &(addr.sin_addr));
+#else
+  int res = inet_pton(AF_INET, remoteAddr.c_str(), &(addr.sin_addr));
+#endif
+  if (res != 1) {
+    WPI_ERROR(m_logger, "could not resolve {} address", server);
+    return -1;
+  }
+  addr.sin_port = htons(port);
+
+  // sendto should not block
+  int result = sendto(m_lsd, data.data(), data.size(), 0,
+                      reinterpret_cast<sockaddr*>(&addr), sizeof(addr));
+  return result;
+}
+
+int UDPClient::receive(uint8_t* data_received, int receive_len) {
+  if (m_port == 0) {
+    return -1;  // return if not receiving
+  }
+  return recv(m_lsd, reinterpret_cast<char*>(data_received), receive_len, 0);
+}
+
+int UDPClient::receive(uint8_t* data_received, int receive_len,
+                       SmallVectorImpl<char>* addr_received,
+                       int* port_received) {
+  if (m_port == 0) {
+    return -1;  // return if not receiving
+  }
+
+  struct sockaddr_in remote;
+  socklen_t remote_len = sizeof(remote);
+  std::memset(&remote, 0, sizeof(remote));
+
+  int result =
+      recvfrom(m_lsd, reinterpret_cast<char*>(data_received), receive_len, 0,
+               reinterpret_cast<sockaddr*>(&remote), &remote_len);
+
+  char ip[50];
+#ifdef _WIN32
+  InetNtop(PF_INET, &(remote.sin_addr.s_addr), ip, sizeof(ip) - 1);
+#else
+  inet_ntop(PF_INET, reinterpret_cast<in_addr*>(&(remote.sin_addr.s_addr)), ip,
+            sizeof(ip) - 1);
+#endif
+
+  ip[49] = '\0';
+  int addr_len = std::strlen(ip);
+  addr_received->clear();
+  addr_received->append(&ip[0], &ip[addr_len]);
+
+  *port_received = ntohs(remote.sin_port);
+
+  return result;
+}
+
+int UDPClient::set_timeout(double timeout) {
+  if (timeout < 0) {
+    return -1;
+  }
+  struct timeval tv;
+  tv.tv_sec = timeout;             // truncating will give seconds
+  timeout -= tv.tv_sec;            // remove seconds portion
+  tv.tv_usec = timeout * 1000000;  // fractions of a second to us
+  int ret = setsockopt(m_lsd, SOL_SOCKET, SO_RCVTIMEO,
+                       reinterpret_cast<char*>(&tv), sizeof(tv));
+  if (ret < 0) {
+    WPI_ERROR(m_logger, "set timeout failed");
+  }
+  return ret;
+}
diff --git a/wpinet/src/main/native/cpp/WebSocket.cpp b/wpinet/src/main/native/cpp/WebSocket.cpp
new file mode 100644
index 0000000..ba57925
--- /dev/null
+++ b/wpinet/src/main/native/cpp/WebSocket.cpp
@@ -0,0 +1,707 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#include "wpinet/WebSocket.h"
+
+#include <random>
+
+#include <fmt/format.h>
+#include <wpi/Base64.h>
+#include <wpi/SmallString.h>
+#include <wpi/SmallVector.h>
+#include <wpi/StringExtras.h>
+#include <wpi/raw_ostream.h>
+#include <wpi/sha1.h>
+
+#include "wpinet/HttpParser.h"
+#include "wpinet/raw_uv_ostream.h"
+#include "wpinet/uv/Stream.h"
+
+using namespace wpi;
+
+namespace {
+class WebSocketWriteReq : public uv::WriteReq {
+ public:
+  explicit WebSocketWriteReq(
+      std::function<void(std::span<uv::Buffer>, uv::Error)> callback)
+      : m_callback{std::move(callback)} {
+    finish.connect([this](uv::Error err) {
+      for (auto&& buf : m_internalBufs) {
+        buf.Deallocate();
+      }
+      m_callback(m_userBufs, err);
+    });
+  }
+
+  std::function<void(std::span<uv::Buffer>, uv::Error)> m_callback;
+  SmallVector<uv::Buffer, 4> m_internalBufs;
+  SmallVector<uv::Buffer, 4> m_userBufs;
+};
+}  // namespace
+
+static constexpr uint8_t kFlagMasking = 0x80;
+static constexpr uint8_t kLenMask = 0x7f;
+
+class WebSocket::ClientHandshakeData {
+ public:
+  ClientHandshakeData() {
+    // key is a random nonce
+    static std::random_device rd;
+    static std::default_random_engine gen{rd()};
+    std::uniform_int_distribution<unsigned int> dist(0, 255);
+    char nonce[16];  // the nonce sent to the server
+    for (char& v : nonce) {
+      v = static_cast<char>(dist(gen));
+    }
+    raw_svector_ostream os(key);
+    Base64Encode(os, {nonce, 16});
+  }
+  ~ClientHandshakeData() {
+    if (auto t = timer.lock()) {
+      t->Stop();
+      t->Close();
+    }
+  }
+
+  SmallString<64> key;                       // the key sent to the server
+  SmallVector<std::string, 2> protocols;     // valid protocols
+  HttpParser parser{HttpParser::kResponse};  // server response parser
+  bool hasUpgrade = false;
+  bool hasConnection = false;
+  bool hasAccept = false;
+  bool hasProtocol = false;
+
+  std::weak_ptr<uv::Timer> timer;
+};
+
+static std::string_view AcceptHash(std::string_view key,
+                                   SmallVectorImpl<char>& buf) {
+  SHA1 hash;
+  hash.Update(key);
+  hash.Update("258EAFA5-E914-47DA-95CA-C5AB0DC85B11");
+  SmallString<64> hashBuf;
+  return Base64Encode(hash.RawFinal(hashBuf), buf);
+}
+
+WebSocket::WebSocket(uv::Stream& stream, bool server, const private_init&)
+    : m_stream{stream}, m_server{server} {
+  // Connect closed and error signals to ourselves
+  m_stream.closed.connect([this]() { SetClosed(1006, "handle closed"); });
+  m_stream.error.connect([this](uv::Error err) {
+    Terminate(1006, fmt::format("stream error: {}", err.name()));
+  });
+
+  // Start reading
+  m_stream.StopRead();  // we may have been reading
+  m_stream.StartRead();
+  m_stream.data.connect(
+      [this](uv::Buffer& buf, size_t size) { HandleIncoming(buf, size); });
+  m_stream.end.connect(
+      [this]() { Terminate(1006, "remote end closed connection"); });
+}
+
+WebSocket::~WebSocket() = default;
+
+std::shared_ptr<WebSocket> WebSocket::CreateClient(
+    uv::Stream& stream, std::string_view uri, std::string_view host,
+    std::span<const std::string_view> protocols, const ClientOptions& options) {
+  auto ws = std::make_shared<WebSocket>(stream, false, private_init{});
+  stream.SetData(ws);
+  ws->StartClient(uri, host, protocols, options);
+  return ws;
+}
+
+std::shared_ptr<WebSocket> WebSocket::CreateServer(uv::Stream& stream,
+                                                   std::string_view key,
+                                                   std::string_view version,
+                                                   std::string_view protocol) {
+  auto ws = std::make_shared<WebSocket>(stream, true, private_init{});
+  stream.SetData(ws);
+  ws->StartServer(key, version, protocol);
+  return ws;
+}
+
+void WebSocket::Close(uint16_t code, std::string_view reason) {
+  SendClose(code, reason);
+  if (m_state != FAILED && m_state != CLOSED) {
+    m_state = CLOSING;
+  }
+}
+
+void WebSocket::Fail(uint16_t code, std::string_view reason) {
+  if (m_state == FAILED || m_state == CLOSED) {
+    return;
+  }
+  SendClose(code, reason);
+  SetClosed(code, reason, true);
+  Shutdown();
+}
+
+void WebSocket::Terminate(uint16_t code, std::string_view reason) {
+  if (m_state == FAILED || m_state == CLOSED) {
+    return;
+  }
+  SetClosed(code, reason);
+  Shutdown();
+}
+
+void WebSocket::StartClient(std::string_view uri, std::string_view host,
+                            std::span<const std::string_view> protocols,
+                            const ClientOptions& options) {
+  // Create client handshake data
+  m_clientHandshake = std::make_unique<ClientHandshakeData>();
+
+  // Build client request
+  SmallVector<uv::Buffer, 4> bufs;
+  raw_uv_ostream os{bufs, 4096};
+
+  os << "GET " << uri << " HTTP/1.1\r\n";
+  os << "Host: " << host << "\r\n";
+  os << "Upgrade: websocket\r\n";
+  os << "Connection: Upgrade\r\n";
+  os << "Sec-WebSocket-Key: " << m_clientHandshake->key << "\r\n";
+  os << "Sec-WebSocket-Version: 13\r\n";
+
+  // protocols (if provided)
+  if (!protocols.empty()) {
+    os << "Sec-WebSocket-Protocol: ";
+    bool first = true;
+    for (auto protocol : protocols) {
+      if (!first) {
+        os << ", ";
+      } else {
+        first = false;
+      }
+      os << protocol;
+      // also save for later checking against server response
+      m_clientHandshake->protocols.emplace_back(protocol);
+    }
+    os << "\r\n";
+  }
+
+  // other headers
+  for (auto&& header : options.extraHeaders) {
+    os << header.first << ": " << header.second << "\r\n";
+  }
+
+  // finish headers
+  os << "\r\n";
+
+  // Send client request
+  m_stream.Write(bufs, [](auto bufs, uv::Error) {
+    for (auto& buf : bufs) {
+      buf.Deallocate();
+    }
+  });
+
+  // Set up client response handling
+  m_clientHandshake->parser.status.connect([this](std::string_view status) {
+    unsigned int code = m_clientHandshake->parser.GetStatusCode();
+    if (code != 101) {
+      Terminate(code, status);
+    }
+  });
+  m_clientHandshake->parser.header.connect(
+      [this](std::string_view name, std::string_view value) {
+        value = trim(value);
+        if (equals_lower(name, "upgrade")) {
+          if (!equals_lower(value, "websocket")) {
+            return Terminate(1002, "invalid upgrade response value");
+          }
+          m_clientHandshake->hasUpgrade = true;
+        } else if (equals_lower(name, "connection")) {
+          if (!equals_lower(value, "upgrade")) {
+            return Terminate(1002, "invalid connection response value");
+          }
+          m_clientHandshake->hasConnection = true;
+        } else if (equals_lower(name, "sec-websocket-accept")) {
+          // Check against expected response
+          SmallString<64> acceptBuf;
+          if (!equals(value, AcceptHash(m_clientHandshake->key, acceptBuf))) {
+            return Terminate(1002, "invalid accept key");
+          }
+          m_clientHandshake->hasAccept = true;
+        } else if (equals_lower(name, "sec-websocket-extensions")) {
+          // No extensions are supported
+          if (!value.empty()) {
+            return Terminate(1010, "unsupported extension");
+          }
+        } else if (equals_lower(name, "sec-websocket-protocol")) {
+          // Make sure it was one of the provided protocols
+          bool match = false;
+          for (auto&& protocol : m_clientHandshake->protocols) {
+            if (equals_lower(value, protocol)) {
+              match = true;
+              break;
+            }
+          }
+          if (!match) {
+            return Terminate(1003, "unsupported protocol");
+          }
+          m_clientHandshake->hasProtocol = true;
+          m_protocol = value;
+        }
+      });
+  m_clientHandshake->parser.headersComplete.connect([this](bool) {
+    if (!m_clientHandshake->hasUpgrade || !m_clientHandshake->hasConnection ||
+        !m_clientHandshake->hasAccept ||
+        (!m_clientHandshake->hasProtocol &&
+         !m_clientHandshake->protocols.empty())) {
+      return Terminate(1002, "invalid response");
+    }
+    if (m_state == CONNECTING) {
+      m_state = OPEN;
+      open(m_protocol);
+    }
+  });
+
+  // Start handshake timer if a timeout was specified
+  if (options.handshakeTimeout != (uv::Timer::Time::max)()) {
+    auto timer = uv::Timer::Create(m_stream.GetLoopRef());
+    timer->timeout.connect(
+        [this]() { Terminate(1006, "connection timed out"); });
+    timer->Start(options.handshakeTimeout);
+    m_clientHandshake->timer = timer;
+  }
+}
+
+void WebSocket::StartServer(std::string_view key, std::string_view version,
+                            std::string_view protocol) {
+  m_protocol = protocol;
+
+  // Build server response
+  SmallVector<uv::Buffer, 4> bufs;
+  raw_uv_ostream os{bufs, 4096};
+
+  // Handle unsupported version
+  if (version != "13") {
+    os << "HTTP/1.1 426 Upgrade Required\r\n";
+    os << "Upgrade: WebSocket\r\n";
+    os << "Sec-WebSocket-Version: 13\r\n\r\n";
+    m_stream.Write(bufs, [this](auto bufs, uv::Error) {
+      for (auto& buf : bufs) {
+        buf.Deallocate();
+      }
+      // XXX: Should we support sending a new handshake on the same connection?
+      // XXX: "this->" is required by GCC 5.5 (bug)
+      this->Terminate(1003, "unsupported protocol version");
+    });
+    return;
+  }
+
+  os << "HTTP/1.1 101 Switching Protocols\r\n";
+  os << "Upgrade: websocket\r\n";
+  os << "Connection: Upgrade\r\n";
+
+  // accept hash
+  SmallString<64> acceptBuf;
+  os << "Sec-WebSocket-Accept: " << AcceptHash(key, acceptBuf) << "\r\n";
+
+  if (!protocol.empty()) {
+    os << "Sec-WebSocket-Protocol: " << protocol << "\r\n";
+  }
+
+  // end headers
+  os << "\r\n";
+
+  // Send server response
+  m_stream.Write(bufs, [this](auto bufs, uv::Error) {
+    for (auto& buf : bufs) {
+      buf.Deallocate();
+    }
+    if (m_state == CONNECTING) {
+      m_state = OPEN;
+      open(m_protocol);
+    }
+  });
+}
+
+void WebSocket::SendClose(uint16_t code, std::string_view reason) {
+  SmallVector<uv::Buffer, 4> bufs;
+  if (code != 1005) {
+    raw_uv_ostream os{bufs, 4096};
+    const uint8_t codeMsb[] = {static_cast<uint8_t>((code >> 8) & 0xff),
+                               static_cast<uint8_t>(code & 0xff)};
+    os << std::span{codeMsb};
+    os << reason;
+  }
+  Send(kFlagFin | kOpClose, bufs, [](auto bufs, uv::Error) {
+    for (auto&& buf : bufs) {
+      buf.Deallocate();
+    }
+  });
+}
+
+void WebSocket::SetClosed(uint16_t code, std::string_view reason, bool failed) {
+  if (m_state == FAILED || m_state == CLOSED) {
+    return;
+  }
+  m_state = failed ? FAILED : CLOSED;
+  closed(code, reason);
+}
+
+void WebSocket::Shutdown() {
+  m_stream.Shutdown([this] { m_stream.Close(); });
+}
+
+void WebSocket::HandleIncoming(uv::Buffer& buf, size_t size) {
+  // ignore incoming data if we're failed or closed
+  if (m_state == FAILED || m_state == CLOSED) {
+    return;
+  }
+
+  std::string_view data{buf.base, size};
+
+  // Handle connecting state (mainly on client)
+  if (m_state == CONNECTING) {
+    if (m_clientHandshake) {
+      data = m_clientHandshake->parser.Execute(data);
+      // check for parser failure
+      if (m_clientHandshake->parser.HasError()) {
+        return Terminate(1003, "invalid response");
+      }
+      if (m_state != OPEN) {
+        return;  // not done with handshake yet
+      }
+
+      // we're done with the handshake, so release its memory
+      m_clientHandshake.reset();
+
+      // fall through to process additional data after handshake
+    } else {
+      return Terminate(1003, "got data on server before response");
+    }
+  }
+
+  // Message processing
+  while (!data.empty()) {
+    if (m_frameSize == UINT64_MAX) {
+      // Need at least two bytes to determine header length
+      if (m_header.size() < 2u) {
+        size_t toCopy = (std::min)(2u - m_header.size(), data.size());
+        m_header.append(data.data(), data.data() + toCopy);
+        data.remove_prefix(toCopy);
+        if (m_header.size() < 2u) {
+          return;  // need more data
+        }
+
+        // Validate RSV bits are zero
+        if ((m_header[0] & 0x70) != 0) {
+          return Fail(1002, "nonzero RSV");
+        }
+      }
+
+      // Once we have first two bytes, we can calculate the header size
+      if (m_headerSize == 0) {
+        m_headerSize = 2;
+        uint8_t len = m_header[1] & kLenMask;
+        if (len == 126) {
+          m_headerSize += 2;
+        } else if (len == 127) {
+          m_headerSize += 8;
+        }
+        bool masking = (m_header[1] & kFlagMasking) != 0;
+        if (masking) {
+          m_headerSize += 4;  // masking key
+        }
+        // On server side, incoming messages MUST be masked
+        // On client side, incoming messages MUST NOT be masked
+        if (m_server && !masking) {
+          return Fail(1002, "client data not masked");
+        }
+        if (!m_server && masking) {
+          return Fail(1002, "server data masked");
+        }
+      }
+
+      // Need to complete header to calculate message size
+      if (m_header.size() < m_headerSize) {
+        size_t toCopy = (std::min)(m_headerSize - m_header.size(), data.size());
+        m_header.append(data.data(), data.data() + toCopy);
+        data.remove_prefix(toCopy);
+        if (m_header.size() < m_headerSize) {
+          return;  // need more data
+        }
+      }
+
+      if (m_header.size() >= m_headerSize) {
+        // get payload length
+        uint8_t len = m_header[1] & kLenMask;
+        if (len == 126) {
+          m_frameSize = (static_cast<uint16_t>(m_header[2]) << 8) |
+                        static_cast<uint16_t>(m_header[3]);
+        } else if (len == 127) {
+          m_frameSize = (static_cast<uint64_t>(m_header[2]) << 56) |
+                        (static_cast<uint64_t>(m_header[3]) << 48) |
+                        (static_cast<uint64_t>(m_header[4]) << 40) |
+                        (static_cast<uint64_t>(m_header[5]) << 32) |
+                        (static_cast<uint64_t>(m_header[6]) << 24) |
+                        (static_cast<uint64_t>(m_header[7]) << 16) |
+                        (static_cast<uint64_t>(m_header[8]) << 8) |
+                        static_cast<uint64_t>(m_header[9]);
+        } else {
+          m_frameSize = len;
+        }
+
+        // limit maximum size
+        if ((m_payload.size() + m_frameSize) > m_maxMessageSize) {
+          return Fail(1009, "message too large");
+        }
+      }
+    }
+
+    if (m_frameSize != UINT64_MAX) {
+      size_t need = m_frameStart + m_frameSize - m_payload.size();
+      size_t toCopy = (std::min)(need, data.size());
+      m_payload.append(data.data(), data.data() + toCopy);
+      data.remove_prefix(toCopy);
+      need -= toCopy;
+      if (need == 0) {
+        // We have a complete frame
+        // If the message had masking, unmask it
+        if ((m_header[1] & kFlagMasking) != 0) {
+          uint8_t key[4] = {
+              m_header[m_headerSize - 4], m_header[m_headerSize - 3],
+              m_header[m_headerSize - 2], m_header[m_headerSize - 1]};
+          int n = 0;
+          for (uint8_t& ch : std::span{m_payload}.subspan(m_frameStart)) {
+            ch ^= key[n++];
+            if (n >= 4) {
+              n = 0;
+            }
+          }
+        }
+
+        // Handle message
+        bool fin = (m_header[0] & kFlagFin) != 0;
+        uint8_t opcode = m_header[0] & kOpMask;
+        switch (opcode) {
+          case kOpCont:
+            switch (m_fragmentOpcode) {
+              case kOpText:
+                if (!m_combineFragments || fin) {
+                  text(std::string_view{reinterpret_cast<char*>(
+                                            m_payload.data()),
+                                        m_payload.size()},
+                       fin);
+                }
+                break;
+              case kOpBinary:
+                if (!m_combineFragments || fin) {
+                  binary(m_payload, fin);
+                }
+                break;
+              default:
+                // no preceding message?
+                return Fail(1002, "invalid continuation message");
+            }
+            if (fin) {
+              m_fragmentOpcode = 0;
+            }
+            break;
+          case kOpText:
+            if (m_fragmentOpcode != 0) {
+              return Fail(1002, "incomplete fragment");
+            }
+            if (!m_combineFragments || fin) {
+#ifdef WPINET_WEBSOCKET_VERBOSE_DEBUG
+              fmt::print(
+                  "WS RecvText({})\n",
+                  std::string_view{reinterpret_cast<char*>(m_payload.data()),
+                                   m_payload.size()});
+#endif
+              text(std::string_view{reinterpret_cast<char*>(m_payload.data()),
+                                    m_payload.size()},
+                   fin);
+            }
+            if (!fin) {
+              m_fragmentOpcode = opcode;
+            }
+            break;
+          case kOpBinary:
+            if (m_fragmentOpcode != 0) {
+              return Fail(1002, "incomplete fragment");
+            }
+            if (!m_combineFragments || fin) {
+#ifdef WPINET_WEBSOCKET_VERBOSE_DEBUG
+              SmallString<128> str;
+              raw_svector_ostream stros{str};
+              for (auto ch : m_payload) {
+                stros << fmt::format("{:02x},",
+                                     static_cast<unsigned int>(ch) & 0xff);
+              }
+              fmt::print("WS RecvBinary({})\n", str.str());
+#endif
+              binary(m_payload, fin);
+            }
+            if (!fin) {
+              m_fragmentOpcode = opcode;
+            }
+            break;
+          case kOpClose: {
+            uint16_t code;
+            std::string_view reason;
+            if (!fin) {
+              code = 1002;
+              reason = "cannot fragment control frames";
+            } else if (m_payload.size() < 2) {
+              code = 1005;
+            } else {
+              code = (static_cast<uint16_t>(m_payload[0]) << 8) |
+                     static_cast<uint16_t>(m_payload[1]);
+              reason = drop_front(
+                  {reinterpret_cast<char*>(m_payload.data()), m_payload.size()},
+                  2);
+            }
+            // Echo the close if we didn't previously send it
+            if (m_state != CLOSING) {
+              SendClose(code, reason);
+            }
+            SetClosed(code, reason);
+            // If we're the server, shutdown the connection.
+            if (m_server) {
+              Shutdown();
+            }
+            break;
+          }
+          case kOpPing:
+            if (!fin) {
+              return Fail(1002, "cannot fragment control frames");
+            }
+            ping(m_payload);
+            break;
+          case kOpPong:
+            if (!fin) {
+              return Fail(1002, "cannot fragment control frames");
+            }
+            pong(m_payload);
+            break;
+          default:
+            return Fail(1002, "invalid message opcode");
+        }
+
+        // Prepare for next message
+        m_header.clear();
+        m_headerSize = 0;
+        if (!m_combineFragments || fin) {
+          m_payload.clear();
+        }
+        m_frameStart = m_payload.size();
+        m_frameSize = UINT64_MAX;
+      }
+    }
+  }
+}
+
+static void WriteFrame(WebSocketWriteReq& req,
+                       SmallVectorImpl<uv::Buffer>& bufs, bool server,
+                       uint8_t opcode, std::span<const uv::Buffer> data) {
+  SmallVector<uv::Buffer, 4> internalBufs;
+  raw_uv_ostream os{internalBufs, 4096};
+
+#ifdef WPINET_WEBSOCKET_VERBOSE_DEBUG
+  if ((opcode & 0x7f) == 0x01) {
+    SmallString<128> str;
+    for (auto&& d : data) {
+      str.append(std::string_view(d.base, d.len));
+    }
+    fmt::print("WS SendText({})\n", str.str());
+  } else if ((opcode & 0x7f) == 0x02) {
+    SmallString<128> str;
+    raw_svector_ostream stros{str};
+    for (auto&& d : data) {
+      for (auto ch : d.data()) {
+        stros << fmt::format("{:02x},", static_cast<unsigned int>(ch) & 0xff);
+      }
+    }
+    fmt::print("WS SendBinary({})\n", str.str());
+  }
+#endif
+
+  // opcode (includes FIN bit)
+  os << static_cast<unsigned char>(opcode);
+
+  // payload length
+  uint64_t size = 0;
+  for (auto&& buf : data) {
+    size += buf.len;
+  }
+  if (size < 126) {
+    os << static_cast<unsigned char>((server ? 0x00 : kFlagMasking) | size);
+  } else if (size <= 0xffff) {
+    os << static_cast<unsigned char>((server ? 0x00 : kFlagMasking) | 126);
+    const uint8_t sizeMsb[] = {static_cast<uint8_t>((size >> 8) & 0xff),
+                               static_cast<uint8_t>(size & 0xff)};
+    os << std::span{sizeMsb};
+  } else {
+    os << static_cast<unsigned char>((server ? 0x00 : kFlagMasking) | 127);
+    const uint8_t sizeMsb[] = {static_cast<uint8_t>((size >> 56) & 0xff),
+                               static_cast<uint8_t>((size >> 48) & 0xff),
+                               static_cast<uint8_t>((size >> 40) & 0xff),
+                               static_cast<uint8_t>((size >> 32) & 0xff),
+                               static_cast<uint8_t>((size >> 24) & 0xff),
+                               static_cast<uint8_t>((size >> 16) & 0xff),
+                               static_cast<uint8_t>((size >> 8) & 0xff),
+                               static_cast<uint8_t>(size & 0xff)};
+    os << std::span{sizeMsb};
+  }
+
+  // clients need to mask the input data
+  if (!server) {
+    // generate masking key
+    static std::random_device rd;
+    static std::default_random_engine gen{rd()};
+    std::uniform_int_distribution<unsigned int> dist(0, 255);
+    uint8_t key[4];
+    for (uint8_t& v : key) {
+      v = dist(gen);
+    }
+    os << std::span<const uint8_t>{key, 4};
+    // copy and mask data
+    int n = 0;
+    for (auto&& buf : data) {
+      for (auto&& ch : buf.data()) {
+        os << static_cast<unsigned char>(static_cast<uint8_t>(ch) ^ key[n++]);
+        if (n >= 4) {
+          n = 0;
+        }
+      }
+    }
+    bufs.append(internalBufs.begin(), internalBufs.end());
+    // don't send the user bufs as we copied their data
+  } else {
+    bufs.append(internalBufs.begin(), internalBufs.end());
+    // servers can just send the buffers directly without masking
+    bufs.append(data.begin(), data.end());
+  }
+  req.m_internalBufs.append(internalBufs.begin(), internalBufs.end());
+  req.m_userBufs.append(data.begin(), data.end());
+}
+
+void WebSocket::SendFrames(
+    std::span<const Frame> frames,
+    std::function<void(std::span<uv::Buffer>, uv::Error)> callback) {
+  // If we're not open, emit an error and don't send the data
+  if (m_state != OPEN) {
+    int err;
+    if (m_state == CONNECTING) {
+      err = UV_EAGAIN;
+    } else {
+      err = UV_ESHUTDOWN;
+    }
+    SmallVector<uv::Buffer, 4> bufs;
+    for (auto&& frame : frames) {
+      bufs.append(frame.data.begin(), frame.data.end());
+    }
+    callback(bufs, uv::Error{err});
+    return;
+  }
+
+  auto req = std::make_shared<WebSocketWriteReq>(std::move(callback));
+  SmallVector<uv::Buffer, 4> bufs;
+  for (auto&& frame : frames) {
+    WriteFrame(*req, bufs, m_server, frame.opcode, frame.data);
+  }
+  m_stream.Write(bufs, req);
+}
diff --git a/wpinet/src/main/native/cpp/WebSocketServer.cpp b/wpinet/src/main/native/cpp/WebSocketServer.cpp
new file mode 100644
index 0000000..09424bb
--- /dev/null
+++ b/wpinet/src/main/native/cpp/WebSocketServer.cpp
@@ -0,0 +1,172 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#include "wpinet/WebSocketServer.h"
+
+#include <utility>
+
+#include <wpi/StringExtras.h>
+#include <wpi/fmt/raw_ostream.h>
+
+#include "wpinet/raw_uv_ostream.h"
+#include "wpinet/uv/Buffer.h"
+#include "wpinet/uv/Stream.h"
+
+using namespace wpi;
+
+WebSocketServerHelper::WebSocketServerHelper(HttpParser& req) {
+  req.header.connect([this](std::string_view name, std::string_view value) {
+    if (equals_lower(name, "host")) {
+      m_gotHost = true;
+    } else if (equals_lower(name, "upgrade")) {
+      if (equals_lower(value, "websocket")) {
+        m_websocket = true;
+      }
+    } else if (equals_lower(name, "sec-websocket-key")) {
+      m_key = value;
+    } else if (equals_lower(name, "sec-websocket-version")) {
+      m_version = value;
+    } else if (equals_lower(name, "sec-websocket-protocol")) {
+      // Protocols are comma delimited, repeated headers add to list
+      SmallVector<std::string_view, 2> protocols;
+      split(value, protocols, ",", -1, false);
+      for (auto protocol : protocols) {
+        protocol = trim(protocol);
+        if (!protocol.empty()) {
+          m_protocols.emplace_back(protocol);
+        }
+      }
+    }
+  });
+  req.headersComplete.connect([&req, this](bool) {
+    if (req.IsUpgrade() && IsUpgrade()) {
+      upgrade();
+    }
+  });
+}
+
+std::pair<bool, std::string_view> WebSocketServerHelper::MatchProtocol(
+    std::span<const std::string_view> protocols) {
+  if (protocols.empty() && m_protocols.empty()) {
+    return {true, {}};
+  }
+  for (auto protocol : protocols) {
+    for (auto&& clientProto : m_protocols) {
+      if (protocol == clientProto) {
+        return {true, protocol};
+      }
+    }
+  }
+  return {false, {}};
+}
+
+WebSocketServer::WebSocketServer(uv::Stream& stream,
+                                 std::span<const std::string_view> protocols,
+                                 ServerOptions options, const private_init&)
+    : m_stream{stream},
+      m_helper{m_req},
+      m_protocols{protocols.begin(), protocols.end()},
+      m_options{std::move(options)} {
+  // Header handling
+  m_req.header.connect([this](std::string_view name, std::string_view value) {
+    if (equals_lower(name, "host")) {
+      if (m_options.checkHost) {
+        if (!m_options.checkHost(value)) {
+          Abort(401, "Unrecognized Host");
+        }
+      }
+    }
+  });
+  m_req.url.connect([this](std::string_view name) {
+    if (m_options.checkUrl) {
+      if (!m_options.checkUrl(name)) {
+        Abort(404, "Not Found");
+      }
+    }
+  });
+  m_req.headersComplete.connect([this](bool) {
+    // We only accept websocket connections
+    if (!m_helper.IsUpgrade() || !m_req.IsUpgrade()) {
+      Abort(426, "Upgrade Required");
+    }
+  });
+
+  // Handle upgrade event
+  m_helper.upgrade.connect([this] {
+    if (m_aborted) {
+      return;
+    }
+
+    // Negotiate sub-protocol
+    SmallVector<std::string_view, 2> protocols{m_protocols.begin(),
+                                               m_protocols.end()};
+    std::string_view protocol = m_helper.MatchProtocol(protocols).second;
+
+    // Disconnect our header reader
+    m_dataConn.disconnect();
+
+    // Accepting the stream may destroy this (as it replaces the stream user
+    // data), so grab a shared pointer first.
+    auto self = shared_from_this();
+
+    // Accept the upgrade
+    auto ws = m_helper.Accept(m_stream, protocol);
+
+    // Connect the websocket open event to our connected event.
+    ws->open.connect_extended(
+        [self, s = ws.get()](auto conn, std::string_view) {
+          self->connected(self->m_req.GetUrl(), *s);
+          conn.disconnect();  // one-shot
+        });
+  });
+
+  // Set up stream
+  stream.StartRead();
+  m_dataConn =
+      stream.data.connect_connection([this](uv::Buffer& buf, size_t size) {
+        if (m_aborted) {
+          return;
+        }
+        m_req.Execute(std::string_view{buf.base, size});
+        if (m_req.HasError()) {
+          Abort(400, "Bad Request");
+        }
+      });
+  m_errorConn =
+      stream.error.connect_connection([this](uv::Error) { m_stream.Close(); });
+  m_endConn = stream.end.connect_connection([this] { m_stream.Close(); });
+}
+
+std::shared_ptr<WebSocketServer> WebSocketServer::Create(
+    uv::Stream& stream, std::span<const std::string_view> protocols,
+    const ServerOptions& options) {
+  auto server = std::make_shared<WebSocketServer>(stream, protocols, options,
+                                                  private_init{});
+  stream.SetData(server);
+  return server;
+}
+
+void WebSocketServer::Abort(uint16_t code, std::string_view reason) {
+  if (m_aborted) {
+    return;
+  }
+  m_aborted = true;
+
+  // Build response
+  SmallVector<uv::Buffer, 4> bufs;
+  raw_uv_ostream os{bufs, 1024};
+
+  // Handle unsupported version
+  fmt::print(os, "HTTP/1.1 {} {}\r\n", code, reason);
+  if (code == 426) {
+    os << "Upgrade: WebSocket\r\n";
+  }
+  os << "\r\n";
+  m_stream.Write(bufs, [this](auto bufs, uv::Error) {
+    for (auto& buf : bufs) {
+      buf.Deallocate();
+    }
+    m_stream.Shutdown([this] { m_stream.Close(); });
+  });
+}
diff --git a/wpinet/src/main/native/cpp/hostname.cpp b/wpinet/src/main/native/cpp/hostname.cpp
new file mode 100644
index 0000000..ae25b29
--- /dev/null
+++ b/wpinet/src/main/native/cpp/hostname.cpp
@@ -0,0 +1,58 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#include "wpinet/hostname.h"
+
+#include <cstdlib>
+#include <string>
+#include <string_view>
+
+#include <wpi/SmallVector.h>
+
+#include "uv.h"
+
+namespace wpi {
+
+std::string GetHostname() {
+  std::string rv;
+  char name[256];
+  size_t size = sizeof(name);
+
+  int err = uv_os_gethostname(name, &size);
+  if (err == 0) {
+    rv.assign(name, size);
+  } else if (err == UV_ENOBUFS) {
+    char* name2 = static_cast<char*>(std::malloc(size));
+    err = uv_os_gethostname(name2, &size);
+    if (err == 0) {
+      rv.assign(name2, size);
+    }
+    std::free(name2);
+  }
+
+  return rv;
+}
+
+std::string_view GetHostname(SmallVectorImpl<char>& name) {
+  // Use a tmp array to not require the SmallVector to be too large.
+  char tmpName[256];
+  size_t size = sizeof(tmpName);
+
+  name.clear();
+
+  int err = uv_os_gethostname(tmpName, &size);
+  if (err == 0) {
+    name.append(tmpName, tmpName + size);
+  } else if (err == UV_ENOBUFS) {
+    name.resize(size);
+    err = uv_os_gethostname(name.data(), &size);
+    if (err != 0) {
+      size = 0;
+    }
+  }
+
+  return {name.data(), size};
+}
+
+}  // namespace wpi
diff --git a/wpinet/src/main/native/cpp/http_parser.cpp b/wpinet/src/main/native/cpp/http_parser.cpp
new file mode 100644
index 0000000..2bec4a7
--- /dev/null
+++ b/wpinet/src/main/native/cpp/http_parser.cpp
@@ -0,0 +1,2475 @@
+/* Copyright Joyent, Inc. and other Node contributors.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+#include "wpinet/http_parser.h"
+#include <assert.h>
+#include <stddef.h>
+#include <ctype.h>
+#include <string.h>
+#include <limits.h>
+
+#ifdef _WIN32
+#pragma warning(disable : 4018 26451)
+#endif
+
+#ifndef ULLONG_MAX
+# define ULLONG_MAX ((uint64_t) -1) /* 2^64-1 */
+#endif
+
+#ifndef MIN
+# define MIN(a,b) ((a) < (b) ? (a) : (b))
+#endif
+
+#ifndef ARRAY_SIZE
+# define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
+#endif
+
+#ifndef BIT_AT
+# define BIT_AT(a, i)                                                \
+  (!!((unsigned int) (a)[(unsigned int) (i) >> 3] &                  \
+   (1 << ((unsigned int) (i) & 7))))
+#endif
+
+#ifndef ELEM_AT
+# define ELEM_AT(a, i, v) ((unsigned int) (i) < ARRAY_SIZE(a) ? (a)[(i)] : (v))
+#endif
+
+#define SET_ERRNO(e)                                                 \
+do {                                                                 \
+  parser->nread = nread;                                             \
+  parser->http_errno = (e);                                          \
+} while(0)
+
+#define CURRENT_STATE() p_state
+#define UPDATE_STATE(V) p_state = (enum state) (V);
+#define RETURN(V)                                                    \
+do {                                                                 \
+  parser->nread = nread;                                             \
+  parser->state = CURRENT_STATE();                                   \
+  return (V);                                                        \
+} while (0);
+#define REEXECUTE()                                                  \
+  goto reexecute;                                                    \
+
+
+#ifdef __GNUC__
+# define LIKELY(X) __builtin_expect(!!(X), 1)
+# define UNLIKELY(X) __builtin_expect(!!(X), 0)
+#else
+# define LIKELY(X) (X)
+# define UNLIKELY(X) (X)
+#endif
+
+
+/* Run the notify callback FOR, returning ER if it fails */
+#define CALLBACK_NOTIFY_(FOR, ER)                                    \
+do {                                                                 \
+  assert(HTTP_PARSER_ERRNO(parser) == HPE_OK);                       \
+                                                                     \
+  if (LIKELY(settings->on_##FOR)) {                                  \
+    parser->state = CURRENT_STATE();                                 \
+    if (UNLIKELY(0 != settings->on_##FOR(parser))) {                 \
+      SET_ERRNO(HPE_CB_##FOR);                                       \
+    }                                                                \
+    UPDATE_STATE(parser->state);                                     \
+                                                                     \
+    /* We either errored above or got paused; get out */             \
+    if (UNLIKELY(HTTP_PARSER_ERRNO(parser) != HPE_OK)) {             \
+      return (ER);                                                   \
+    }                                                                \
+  }                                                                  \
+} while (0)
+
+/* Run the notify callback FOR and consume the current byte */
+#define CALLBACK_NOTIFY(FOR)            CALLBACK_NOTIFY_(FOR, p - data + 1)
+
+/* Run the notify callback FOR and don't consume the current byte */
+#define CALLBACK_NOTIFY_NOADVANCE(FOR)  CALLBACK_NOTIFY_(FOR, p - data)
+
+/* Run data callback FOR with LEN bytes, returning ER if it fails */
+#define CALLBACK_DATA_(FOR, LEN, ER)                                 \
+do {                                                                 \
+  assert(HTTP_PARSER_ERRNO(parser) == HPE_OK);                       \
+                                                                     \
+  if (FOR##_mark) {                                                  \
+    if (LIKELY(settings->on_##FOR)) {                                \
+      parser->state = CURRENT_STATE();                               \
+      if (UNLIKELY(0 !=                                              \
+                   settings->on_##FOR(parser, FOR##_mark, (LEN)))) { \
+        SET_ERRNO(HPE_CB_##FOR);                                     \
+      }                                                              \
+      UPDATE_STATE(parser->state);                                   \
+                                                                     \
+      /* We either errored above or got paused; get out */           \
+      if (UNLIKELY(HTTP_PARSER_ERRNO(parser) != HPE_OK)) {           \
+        return (ER);                                                 \
+      }                                                              \
+    }                                                                \
+    FOR##_mark = NULL;                                               \
+  }                                                                  \
+} while (0)
+
+/* Run the data callback FOR and consume the current byte */
+#define CALLBACK_DATA(FOR)                                           \
+    CALLBACK_DATA_(FOR, p - FOR##_mark, p - data + 1)
+
+/* Run the data callback FOR and don't consume the current byte */
+#define CALLBACK_DATA_NOADVANCE(FOR)                                 \
+    CALLBACK_DATA_(FOR, p - FOR##_mark, p - data)
+
+/* Set the mark FOR; non-destructive if mark is already set */
+#define MARK(FOR)                                                    \
+do {                                                                 \
+  if (!FOR##_mark) {                                                 \
+    FOR##_mark = p;                                                  \
+  }                                                                  \
+} while (0)
+
+/* Don't allow the total size of the HTTP headers (including the status
+ * line) to exceed HTTP_MAX_HEADER_SIZE.  This check is here to protect
+ * embedders against denial-of-service attacks where the attacker feeds
+ * us a never-ending header that the embedder keeps buffering.
+ *
+ * This check is arguably the responsibility of embedders but we're doing
+ * it on the embedder's behalf because most won't bother and this way we
+ * make the web a little safer.  HTTP_MAX_HEADER_SIZE is still far bigger
+ * than any reasonable request or response so this should never affect
+ * day-to-day operation.
+ */
+#define COUNT_HEADER_SIZE(V)                                         \
+do {                                                                 \
+  nread += (V);                                                      \
+  if (UNLIKELY(nread > (HTTP_MAX_HEADER_SIZE))) {                    \
+    SET_ERRNO(HPE_HEADER_OVERFLOW);                                  \
+    goto error;                                                      \
+  }                                                                  \
+} while (0)
+
+
+#define PROXY_CONNECTION "proxy-connection"
+#define CONNECTION "connection"
+#define CONTENT_LENGTH "content-length"
+#define TRANSFER_ENCODING "transfer-encoding"
+#define UPGRADE "upgrade"
+#define CHUNKED "chunked"
+#define KEEP_ALIVE "keep-alive"
+#define CLOSE "close"
+
+namespace wpi {
+
+
+static const char *method_strings[] =
+  {
+#define XX(num, name, string) #string,
+  HTTP_METHOD_MAP(XX)
+#undef XX
+  };
+
+
+/* Tokens as defined by rfc 2616. Also lowercases them.
+ *        token       = 1*<any CHAR except CTLs or separators>
+ *     separators     = "(" | ")" | "<" | ">" | "@"
+ *                    | "," | ";" | ":" | "\" | <">
+ *                    | "/" | "[" | "]" | "?" | "="
+ *                    | "{" | "}" | SP | HT
+ */
+static const char tokens[256] = {
+/*   0 nul    1 soh    2 stx    3 etx    4 eot    5 enq    6 ack    7 bel  */
+        0,       0,       0,       0,       0,       0,       0,       0,
+/*   8 bs     9 ht    10 nl    11 vt    12 np    13 cr    14 so    15 si   */
+        0,       0,       0,       0,       0,       0,       0,       0,
+/*  16 dle   17 dc1   18 dc2   19 dc3   20 dc4   21 nak   22 syn   23 etb */
+        0,       0,       0,       0,       0,       0,       0,       0,
+/*  24 can   25 em    26 sub   27 esc   28 fs    29 gs    30 rs    31 us  */
+        0,       0,       0,       0,       0,       0,       0,       0,
+/*  32 sp    33  !    34  "    35  #    36  $    37  %    38  &    39  '  */
+       ' ',     '!',      0,      '#',     '$',     '%',     '&',    '\'',
+/*  40  (    41  )    42  *    43  +    44  ,    45  -    46  .    47  /  */
+        0,       0,      '*',     '+',      0,      '-',     '.',      0,
+/*  48  0    49  1    50  2    51  3    52  4    53  5    54  6    55  7  */
+       '0',     '1',     '2',     '3',     '4',     '5',     '6',     '7',
+/*  56  8    57  9    58  :    59  ;    60  <    61  =    62  >    63  ?  */
+       '8',     '9',      0,       0,       0,       0,       0,       0,
+/*  64  @    65  A    66  B    67  C    68  D    69  E    70  F    71  G  */
+        0,      'a',     'b',     'c',     'd',     'e',     'f',     'g',
+/*  72  H    73  I    74  J    75  K    76  L    77  M    78  N    79  O  */
+       'h',     'i',     'j',     'k',     'l',     'm',     'n',     'o',
+/*  80  P    81  Q    82  R    83  S    84  T    85  U    86  V    87  W  */
+       'p',     'q',     'r',     's',     't',     'u',     'v',     'w',
+/*  88  X    89  Y    90  Z    91  [    92  \    93  ]    94  ^    95  _  */
+       'x',     'y',     'z',      0,       0,       0,      '^',     '_',
+/*  96  `    97  a    98  b    99  c   100  d   101  e   102  f   103  g  */
+       '`',     'a',     'b',     'c',     'd',     'e',     'f',     'g',
+/* 104  h   105  i   106  j   107  k   108  l   109  m   110  n   111  o  */
+       'h',     'i',     'j',     'k',     'l',     'm',     'n',     'o',
+/* 112  p   113  q   114  r   115  s   116  t   117  u   118  v   119  w  */
+       'p',     'q',     'r',     's',     't',     'u',     'v',     'w',
+/* 120  x   121  y   122  z   123  {   124  |   125  }   126  ~   127 del */
+       'x',     'y',     'z',      0,      '|',      0,      '~',       0 };
+
+
+static const int8_t unhex[256] =
+  {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
+  ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
+  ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
+  , 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1,-1,-1,-1,-1
+  ,-1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1
+  ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
+  ,-1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1
+  ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
+  };
+
+
+#if HTTP_PARSER_STRICT
+# define T(v) 0
+#else
+# define T(v) v
+#endif
+
+
+static const uint8_t normal_url_char[32] = {
+/*   0 nul    1 soh    2 stx    3 etx    4 eot    5 enq    6 ack    7 bel  */
+        0    |   0    |   0    |   0    |   0    |   0    |   0    |   0,
+/*   8 bs     9 ht    10 nl    11 vt    12 np    13 cr    14 so    15 si   */
+        0    | T(2)   |   0    |   0    | T(16)  |   0    |   0    |   0,
+/*  16 dle   17 dc1   18 dc2   19 dc3   20 dc4   21 nak   22 syn   23 etb */
+        0    |   0    |   0    |   0    |   0    |   0    |   0    |   0,
+/*  24 can   25 em    26 sub   27 esc   28 fs    29 gs    30 rs    31 us  */
+        0    |   0    |   0    |   0    |   0    |   0    |   0    |   0,
+/*  32 sp    33  !    34  "    35  #    36  $    37  %    38  &    39  '  */
+        0    |   2    |   4    |   0    |   16   |   32   |   64   |  128,
+/*  40  (    41  )    42  *    43  +    44  ,    45  -    46  .    47  /  */
+        1    |   2    |   4    |   8    |   16   |   32   |   64   |  128,
+/*  48  0    49  1    50  2    51  3    52  4    53  5    54  6    55  7  */
+        1    |   2    |   4    |   8    |   16   |   32   |   64   |  128,
+/*  56  8    57  9    58  :    59  ;    60  <    61  =    62  >    63  ?  */
+        1    |   2    |   4    |   8    |   16   |   32   |   64   |   0,
+/*  64  @    65  A    66  B    67  C    68  D    69  E    70  F    71  G  */
+        1    |   2    |   4    |   8    |   16   |   32   |   64   |  128,
+/*  72  H    73  I    74  J    75  K    76  L    77  M    78  N    79  O  */
+        1    |   2    |   4    |   8    |   16   |   32   |   64   |  128,
+/*  80  P    81  Q    82  R    83  S    84  T    85  U    86  V    87  W  */
+        1    |   2    |   4    |   8    |   16   |   32   |   64   |  128,
+/*  88  X    89  Y    90  Z    91  [    92  \    93  ]    94  ^    95  _  */
+        1    |   2    |   4    |   8    |   16   |   32   |   64   |  128,
+/*  96  `    97  a    98  b    99  c   100  d   101  e   102  f   103  g  */
+        1    |   2    |   4    |   8    |   16   |   32   |   64   |  128,
+/* 104  h   105  i   106  j   107  k   108  l   109  m   110  n   111  o  */
+        1    |   2    |   4    |   8    |   16   |   32   |   64   |  128,
+/* 112  p   113  q   114  r   115  s   116  t   117  u   118  v   119  w  */
+        1    |   2    |   4    |   8    |   16   |   32   |   64   |  128,
+/* 120  x   121  y   122  z   123  {   124  |   125  }   126  ~   127 del */
+        1    |   2    |   4    |   8    |   16   |   32   |   64   |   0, };
+
+#undef T
+
+enum state
+  { s_dead = 1 /* important that this is > 0 */
+
+  , s_start_req_or_res
+  , s_res_or_resp_H
+  , s_start_res
+  , s_res_H
+  , s_res_HT
+  , s_res_HTT
+  , s_res_HTTP
+  , s_res_http_major
+  , s_res_http_dot
+  , s_res_http_minor
+  , s_res_http_end
+  , s_res_first_status_code
+  , s_res_status_code
+  , s_res_status_start
+  , s_res_status
+  , s_res_line_almost_done
+
+  , s_start_req
+
+  , s_req_method
+  , s_req_spaces_before_url
+  , s_req_schema
+  , s_req_schema_slash
+  , s_req_schema_slash_slash
+  , s_req_server_start
+  , s_req_server
+  , s_req_server_with_at
+  , s_req_path
+  , s_req_query_string_start
+  , s_req_query_string
+  , s_req_fragment_start
+  , s_req_fragment
+  , s_req_http_start
+  , s_req_http_H
+  , s_req_http_HT
+  , s_req_http_HTT
+  , s_req_http_HTTP
+  , s_req_http_major
+  , s_req_http_dot
+  , s_req_http_minor
+  , s_req_http_end
+  , s_req_line_almost_done
+
+  , s_header_field_start
+  , s_header_field
+  , s_header_value_discard_ws
+  , s_header_value_discard_ws_almost_done
+  , s_header_value_discard_lws
+  , s_header_value_start
+  , s_header_value
+  , s_header_value_lws
+
+  , s_header_almost_done
+
+  , s_chunk_size_start
+  , s_chunk_size
+  , s_chunk_parameters
+  , s_chunk_size_almost_done
+
+  , s_headers_almost_done
+  , s_headers_done
+
+  /* Important: 's_headers_done' must be the last 'header' state. All
+   * states beyond this must be 'body' states. It is used for overflow
+   * checking. See the PARSING_HEADER() macro.
+   */
+
+  , s_chunk_data
+  , s_chunk_data_almost_done
+  , s_chunk_data_done
+
+  , s_body_identity
+  , s_body_identity_eof
+
+  , s_message_done
+  };
+
+
+#define PARSING_HEADER(state) (state <= s_headers_done)
+
+
+enum header_states
+  { h_general = 0
+  , h_C
+  , h_CO
+  , h_CON
+
+  , h_matching_connection
+  , h_matching_proxy_connection
+  , h_matching_content_length
+  , h_matching_transfer_encoding
+  , h_matching_upgrade
+
+  , h_connection
+  , h_content_length
+  , h_content_length_num
+  , h_content_length_ws
+  , h_transfer_encoding
+  , h_upgrade
+
+  , h_matching_transfer_encoding_chunked
+  , h_matching_connection_token_start
+  , h_matching_connection_keep_alive
+  , h_matching_connection_close
+  , h_matching_connection_upgrade
+  , h_matching_connection_token
+
+  , h_transfer_encoding_chunked
+  , h_connection_keep_alive
+  , h_connection_close
+  , h_connection_upgrade
+  };
+
+enum http_host_state
+  {
+    s_http_host_dead = 1
+  , s_http_userinfo_start
+  , s_http_userinfo
+  , s_http_host_start
+  , s_http_host_v6_start
+  , s_http_host
+  , s_http_host_v6
+  , s_http_host_v6_end
+  , s_http_host_v6_zone_start
+  , s_http_host_v6_zone
+  , s_http_host_port_start
+  , s_http_host_port
+};
+
+/* Macros for character classes; depends on strict-mode  */
+#define CR                  '\r'
+#define LF                  '\n'
+#define LOWER(c)            (unsigned char)(c | 0x20)
+#define IS_ALPHA(c)         (LOWER(c) >= 'a' && LOWER(c) <= 'z')
+#define IS_NUM(c)           ((c) >= '0' && (c) <= '9')
+#define IS_ALPHANUM(c)      (IS_ALPHA(c) || IS_NUM(c))
+#define IS_HEX(c)           (IS_NUM(c) || (LOWER(c) >= 'a' && LOWER(c) <= 'f'))
+#define IS_MARK(c)          ((c) == '-' || (c) == '_' || (c) == '.' || \
+  (c) == '!' || (c) == '~' || (c) == '*' || (c) == '\'' || (c) == '(' || \
+  (c) == ')')
+#define IS_USERINFO_CHAR(c) (IS_ALPHANUM(c) || IS_MARK(c) || (c) == '%' || \
+  (c) == ';' || (c) == ':' || (c) == '&' || (c) == '=' || (c) == '+' || \
+  (c) == '$' || (c) == ',')
+
+#define STRICT_TOKEN(c)     ((c == ' ') ? 0 : tokens[(unsigned char)c])
+
+#if HTTP_PARSER_STRICT
+#define TOKEN(c)            STRICT_TOKEN(c)
+#define IS_URL_CHAR(c)      (BIT_AT(normal_url_char, (unsigned char)c))
+#define IS_HOST_CHAR(c)     (IS_ALPHANUM(c) || (c) == '.' || (c) == '-')
+#else
+#define TOKEN(c)            tokens[(unsigned char)c]
+#define IS_URL_CHAR(c)                                                         \
+  (BIT_AT(normal_url_char, (unsigned char)c) || ((c) & 0x80))
+#define IS_HOST_CHAR(c)                                                        \
+  (IS_ALPHANUM(c) || (c) == '.' || (c) == '-' || (c) == '_')
+#endif
+
+/**
+ * Verify that a char is a valid visible (printable) US-ASCII
+ * character or %x80-FF
+ **/
+#define IS_HEADER_CHAR(ch)                                                     \
+  (ch == CR || ch == LF || ch == 9 || ((unsigned char)ch > 31 && ch != 127))
+
+#define start_state (parser->type == HTTP_REQUEST ? s_start_req : s_start_res)
+
+
+#if HTTP_PARSER_STRICT
+# define STRICT_CHECK(cond)                                          \
+do {                                                                 \
+  if (cond) {                                                        \
+    SET_ERRNO(HPE_STRICT);                                           \
+    goto error;                                                      \
+  }                                                                  \
+} while (0)
+# define NEW_MESSAGE() (http_should_keep_alive(parser) ? start_state : s_dead)
+#else
+# define STRICT_CHECK(cond)
+# define NEW_MESSAGE() start_state
+#endif
+
+
+/* Map errno values to strings for human-readable output */
+#define HTTP_STRERROR_GEN(n, s) { "HPE_" #n, s },
+static struct {
+  const char *name;
+  const char *description;
+} http_strerror_tab[] = {
+  HTTP_ERRNO_MAP(HTTP_STRERROR_GEN)
+};
+#undef HTTP_STRERROR_GEN
+
+int http_message_needs_eof(const http_parser *parser);
+
+/* Our URL parser.
+ *
+ * This is designed to be shared by http_parser_execute() for URL validation,
+ * hence it has a state transition + byte-for-byte interface. In addition, it
+ * is meant to be embedded in http_parser_parse_url(), which does the dirty
+ * work of turning state transitions URL components for its API.
+ *
+ * This function should only be invoked with non-space characters. It is
+ * assumed that the caller cares about (and can detect) the transition between
+ * URL and non-URL states by looking for these.
+ */
+static enum state
+parse_url_char(enum state s, const char ch)
+{
+  if (ch == ' ' || ch == '\r' || ch == '\n') {
+    return s_dead;
+  }
+
+#if HTTP_PARSER_STRICT
+  if (ch == '\t' || ch == '\f') {
+    return s_dead;
+  }
+#endif
+
+  switch (s) {
+    case s_req_spaces_before_url:
+      /* Proxied requests are followed by scheme of an absolute URI (alpha).
+       * All methods except CONNECT are followed by '/' or '*'.
+       */
+
+      if (ch == '/' || ch == '*') {
+        return s_req_path;
+      }
+
+      if (IS_ALPHA(ch)) {
+        return s_req_schema;
+      }
+
+      break;
+
+    case s_req_schema:
+      if (IS_ALPHA(ch)) {
+        return s;
+      }
+
+      if (ch == ':') {
+        return s_req_schema_slash;
+      }
+
+      break;
+
+    case s_req_schema_slash:
+      if (ch == '/') {
+        return s_req_schema_slash_slash;
+      }
+
+      break;
+
+    case s_req_schema_slash_slash:
+      if (ch == '/') {
+        return s_req_server_start;
+      }
+
+      break;
+
+    case s_req_server_with_at:
+      if (ch == '@') {
+        return s_dead;
+      }
+
+    /* fall through */
+    case s_req_server_start:
+    case s_req_server:
+      if (ch == '/') {
+        return s_req_path;
+      }
+
+      if (ch == '?') {
+        return s_req_query_string_start;
+      }
+
+      if (ch == '@') {
+        return s_req_server_with_at;
+      }
+
+      if (IS_USERINFO_CHAR(ch) || ch == '[' || ch == ']') {
+        return s_req_server;
+      }
+
+      break;
+
+    case s_req_path:
+      if (IS_URL_CHAR(ch)) {
+        return s;
+      }
+
+      switch (ch) {
+        case '?':
+          return s_req_query_string_start;
+
+        case '#':
+          return s_req_fragment_start;
+      }
+
+      break;
+
+    case s_req_query_string_start:
+    case s_req_query_string:
+      if (IS_URL_CHAR(ch)) {
+        return s_req_query_string;
+      }
+
+      switch (ch) {
+        case '?':
+          /* allow extra '?' in query string */
+          return s_req_query_string;
+
+        case '#':
+          return s_req_fragment_start;
+      }
+
+      break;
+
+    case s_req_fragment_start:
+      if (IS_URL_CHAR(ch)) {
+        return s_req_fragment;
+      }
+
+      switch (ch) {
+        case '?':
+          return s_req_fragment;
+
+        case '#':
+          return s;
+      }
+
+      break;
+
+    case s_req_fragment:
+      if (IS_URL_CHAR(ch)) {
+        return s;
+      }
+
+      switch (ch) {
+        case '?':
+        case '#':
+          return s;
+      }
+
+      break;
+
+    default:
+      break;
+  }
+
+  /* We should never fall out of the switch above unless there's an error */
+  return s_dead;
+}
+
+size_t http_parser_execute (http_parser *parser,
+                            const http_parser_settings *settings,
+                            const char *data,
+                            size_t len)
+{
+  char c, ch;
+  int8_t unhex_val;
+  const char *p = data;
+  const char *header_field_mark = 0;
+  const char *header_value_mark = 0;
+  const char *url_mark = 0;
+  const char *body_mark = 0;
+  const char *status_mark = 0;
+  enum state p_state = (enum state) parser->state;
+  const unsigned int lenient = parser->lenient_http_headers;
+  uint32_t nread = parser->nread;
+
+  /* We're in an error state. Don't bother doing anything. */
+  if (HTTP_PARSER_ERRNO(parser) != HPE_OK) {
+    return 0;
+  }
+
+  if (len == 0) {
+    switch (CURRENT_STATE()) {
+      case s_body_identity_eof:
+        /* Use of CALLBACK_NOTIFY() here would erroneously return 1 byte read if
+         * we got paused.
+         */
+        CALLBACK_NOTIFY_NOADVANCE(message_complete);
+        return 0;
+
+      case s_dead:
+      case s_start_req_or_res:
+      case s_start_res:
+      case s_start_req:
+        return 0;
+
+      default:
+        SET_ERRNO(HPE_INVALID_EOF_STATE);
+        return 1;
+    }
+  }
+
+
+  if (CURRENT_STATE() == s_header_field)
+    header_field_mark = data;
+  if (CURRENT_STATE() == s_header_value)
+    header_value_mark = data;
+  switch (CURRENT_STATE()) {
+  case s_req_path:
+  case s_req_schema:
+  case s_req_schema_slash:
+  case s_req_schema_slash_slash:
+  case s_req_server_start:
+  case s_req_server:
+  case s_req_server_with_at:
+  case s_req_query_string_start:
+  case s_req_query_string:
+  case s_req_fragment_start:
+  case s_req_fragment:
+    url_mark = data;
+    break;
+  case s_res_status:
+    status_mark = data;
+    break;
+  default:
+    break;
+  }
+
+  for (p=data; p != data + len; p++) {
+    ch = *p;
+
+    if (PARSING_HEADER(CURRENT_STATE()))
+      COUNT_HEADER_SIZE(1);
+
+reexecute:
+    switch (CURRENT_STATE()) {
+
+      case s_dead:
+        /* this state is used after a 'Connection: close' message
+         * the parser will error out if it reads another message
+         */
+        if (LIKELY(ch == CR || ch == LF))
+          break;
+
+        SET_ERRNO(HPE_CLOSED_CONNECTION);
+        goto error;
+
+      case s_start_req_or_res:
+      {
+        if (ch == CR || ch == LF)
+          break;
+        parser->flags = 0;
+        parser->content_length = ULLONG_MAX;
+
+        if (ch == 'H') {
+          UPDATE_STATE(s_res_or_resp_H);
+
+          CALLBACK_NOTIFY(message_begin);
+        } else {
+          parser->type = HTTP_REQUEST;
+          UPDATE_STATE(s_start_req);
+          REEXECUTE();
+        }
+
+        break;
+      }
+
+      case s_res_or_resp_H:
+        if (ch == 'T') {
+          parser->type = HTTP_RESPONSE;
+          UPDATE_STATE(s_res_HT);
+        } else {
+          if (UNLIKELY(ch != 'E')) {
+            SET_ERRNO(HPE_INVALID_CONSTANT);
+            goto error;
+          }
+
+          parser->type = HTTP_REQUEST;
+          parser->method = HTTP_HEAD;
+          parser->index = 2;
+          UPDATE_STATE(s_req_method);
+        }
+        break;
+
+      case s_start_res:
+      {
+        parser->flags = 0;
+        parser->content_length = ULLONG_MAX;
+
+        switch (ch) {
+          case 'H':
+            UPDATE_STATE(s_res_H);
+            break;
+
+          case CR:
+          case LF:
+            break;
+
+          default:
+            SET_ERRNO(HPE_INVALID_CONSTANT);
+            goto error;
+        }
+
+        CALLBACK_NOTIFY(message_begin);
+        break;
+      }
+
+      case s_res_H:
+        STRICT_CHECK(ch != 'T');
+        UPDATE_STATE(s_res_HT);
+        break;
+
+      case s_res_HT:
+        STRICT_CHECK(ch != 'T');
+        UPDATE_STATE(s_res_HTT);
+        break;
+
+      case s_res_HTT:
+        STRICT_CHECK(ch != 'P');
+        UPDATE_STATE(s_res_HTTP);
+        break;
+
+      case s_res_HTTP:
+        STRICT_CHECK(ch != '/');
+        UPDATE_STATE(s_res_http_major);
+        break;
+
+      case s_res_http_major:
+        if (UNLIKELY(!IS_NUM(ch))) {
+          SET_ERRNO(HPE_INVALID_VERSION);
+          goto error;
+        }
+
+        parser->http_major = ch - '0';
+        UPDATE_STATE(s_res_http_dot);
+        break;
+
+      case s_res_http_dot:
+      {
+        if (UNLIKELY(ch != '.')) {
+          SET_ERRNO(HPE_INVALID_VERSION);
+          goto error;
+        }
+
+        UPDATE_STATE(s_res_http_minor);
+        break;
+      }
+
+      case s_res_http_minor:
+        if (UNLIKELY(!IS_NUM(ch))) {
+          SET_ERRNO(HPE_INVALID_VERSION);
+          goto error;
+        }
+
+        parser->http_minor = ch - '0';
+        UPDATE_STATE(s_res_http_end);
+        break;
+
+      case s_res_http_end:
+      {
+        if (UNLIKELY(ch != ' ')) {
+          SET_ERRNO(HPE_INVALID_VERSION);
+          goto error;
+        }
+
+        UPDATE_STATE(s_res_first_status_code);
+        break;
+      }
+
+      case s_res_first_status_code:
+      {
+        if (!IS_NUM(ch)) {
+          if (ch == ' ') {
+            break;
+          }
+
+          SET_ERRNO(HPE_INVALID_STATUS);
+          goto error;
+        }
+        parser->status_code = ch - '0';
+        UPDATE_STATE(s_res_status_code);
+        break;
+      }
+
+      case s_res_status_code:
+      {
+        if (!IS_NUM(ch)) {
+          switch (ch) {
+            case ' ':
+              UPDATE_STATE(s_res_status_start);
+              break;
+            case CR:
+            case LF:
+              UPDATE_STATE(s_res_status_start);
+              REEXECUTE();
+              break;
+            default:
+              SET_ERRNO(HPE_INVALID_STATUS);
+              goto error;
+          }
+          break;
+        }
+
+        parser->status_code *= 10;
+        parser->status_code += ch - '0';
+
+        if (UNLIKELY(parser->status_code > 999)) {
+          SET_ERRNO(HPE_INVALID_STATUS);
+          goto error;
+        }
+
+        break;
+      }
+
+      case s_res_status_start:
+      {
+        MARK(status);
+        UPDATE_STATE(s_res_status);
+        parser->index = 0;
+
+        if (ch == CR || ch == LF)
+          REEXECUTE();
+
+        break;
+      }
+
+      case s_res_status:
+        if (ch == CR) {
+          UPDATE_STATE(s_res_line_almost_done);
+          CALLBACK_DATA(status);
+          break;
+        }
+
+        if (ch == LF) {
+          UPDATE_STATE(s_header_field_start);
+          CALLBACK_DATA(status);
+          break;
+        }
+
+        break;
+
+      case s_res_line_almost_done:
+        STRICT_CHECK(ch != LF);
+        UPDATE_STATE(s_header_field_start);
+        break;
+
+      case s_start_req:
+      {
+        if (ch == CR || ch == LF)
+          break;
+        parser->flags = 0;
+        parser->content_length = ULLONG_MAX;
+
+        if (UNLIKELY(!IS_ALPHA(ch))) {
+          SET_ERRNO(HPE_INVALID_METHOD);
+          goto error;
+        }
+
+        parser->method = (enum http_method) 0;
+        parser->index = 1;
+        switch (ch) {
+          case 'A': parser->method = HTTP_ACL; break;
+          case 'B': parser->method = HTTP_BIND; break;
+          case 'C': parser->method = HTTP_CONNECT; /* or COPY, CHECKOUT */ break;
+          case 'D': parser->method = HTTP_DELETE; break;
+          case 'G': parser->method = HTTP_GET; break;
+          case 'H': parser->method = HTTP_HEAD; break;
+          case 'L': parser->method = HTTP_LOCK; /* or LINK */ break;
+          case 'M': parser->method = HTTP_MKCOL; /* or MOVE, MKACTIVITY, MERGE, M-SEARCH, MKCALENDAR */ break;
+          case 'N': parser->method = HTTP_NOTIFY; break;
+          case 'O': parser->method = HTTP_OPTIONS; break;
+          case 'P': parser->method = HTTP_POST;
+            /* or PROPFIND|PROPPATCH|PUT|PATCH|PURGE */
+            break;
+          case 'R': parser->method = HTTP_REPORT; /* or REBIND */ break;
+          case 'S': parser->method = HTTP_SUBSCRIBE; /* or SEARCH, SOURCE */ break;
+          case 'T': parser->method = HTTP_TRACE; break;
+          case 'U': parser->method = HTTP_UNLOCK; /* or UNSUBSCRIBE, UNBIND, UNLINK */ break;
+          default:
+            SET_ERRNO(HPE_INVALID_METHOD);
+            goto error;
+        }
+        UPDATE_STATE(s_req_method);
+
+        CALLBACK_NOTIFY(message_begin);
+
+        break;
+      }
+
+      case s_req_method:
+      {
+        const char *matcher;
+        if (UNLIKELY(ch == '\0')) {
+          SET_ERRNO(HPE_INVALID_METHOD);
+          goto error;
+        }
+
+        matcher = method_strings[parser->method];
+        if (ch == ' ' && matcher[parser->index] == '\0') {
+          UPDATE_STATE(s_req_spaces_before_url);
+        } else if (ch == matcher[parser->index]) {
+          ; /* nada */
+        } else if ((ch >= 'A' && ch <= 'Z') || ch == '-') {
+
+          switch (parser->method << 16 | parser->index << 8 | ch) {
+#define XX(meth, pos, ch, new_meth) \
+            case (HTTP_##meth << 16 | pos << 8 | ch): \
+              parser->method = HTTP_##new_meth; break;
+
+            XX(POST,      1, 'U', PUT)
+            XX(POST,      1, 'A', PATCH)
+            XX(POST,      1, 'R', PROPFIND)
+            XX(PUT,       2, 'R', PURGE)
+            XX(CONNECT,   1, 'H', CHECKOUT)
+            XX(CONNECT,   2, 'P', COPY)
+            XX(MKCOL,     1, 'O', MOVE)
+            XX(MKCOL,     1, 'E', MERGE)
+            XX(MKCOL,     1, '-', MSEARCH)
+            XX(MKCOL,     2, 'A', MKACTIVITY)
+            XX(MKCOL,     3, 'A', MKCALENDAR)
+            XX(SUBSCRIBE, 1, 'E', SEARCH)
+            XX(SUBSCRIBE, 1, 'O', SOURCE)
+            XX(REPORT,    2, 'B', REBIND)
+            XX(PROPFIND,  4, 'P', PROPPATCH)
+            XX(LOCK,      1, 'I', LINK)
+            XX(UNLOCK,    2, 'S', UNSUBSCRIBE)
+            XX(UNLOCK,    2, 'B', UNBIND)
+            XX(UNLOCK,    3, 'I', UNLINK)
+#undef XX
+            default:
+              SET_ERRNO(HPE_INVALID_METHOD);
+              goto error;
+          }
+        } else {
+          SET_ERRNO(HPE_INVALID_METHOD);
+          goto error;
+        }
+
+        ++parser->index;
+        break;
+      }
+
+      case s_req_spaces_before_url:
+      {
+        if (ch == ' ') break;
+
+        MARK(url);
+        if (parser->method == HTTP_CONNECT) {
+          UPDATE_STATE(s_req_server_start);
+        }
+
+        UPDATE_STATE(parse_url_char(CURRENT_STATE(), ch));
+        if (UNLIKELY(CURRENT_STATE() == s_dead)) {
+          SET_ERRNO(HPE_INVALID_URL);
+          goto error;
+        }
+
+        break;
+      }
+
+      case s_req_schema:
+      case s_req_schema_slash:
+      case s_req_schema_slash_slash:
+      case s_req_server_start:
+      {
+        switch (ch) {
+          /* No whitespace allowed here */
+          case ' ':
+          case CR:
+          case LF:
+            SET_ERRNO(HPE_INVALID_URL);
+            goto error;
+          default:
+            UPDATE_STATE(parse_url_char(CURRENT_STATE(), ch));
+            if (UNLIKELY(CURRENT_STATE() == s_dead)) {
+              SET_ERRNO(HPE_INVALID_URL);
+              goto error;
+            }
+        }
+
+        break;
+      }
+
+      case s_req_server:
+      case s_req_server_with_at:
+      case s_req_path:
+      case s_req_query_string_start:
+      case s_req_query_string:
+      case s_req_fragment_start:
+      case s_req_fragment:
+      {
+        switch (ch) {
+          case ' ':
+            UPDATE_STATE(s_req_http_start);
+            CALLBACK_DATA(url);
+            break;
+          case CR:
+          case LF:
+            parser->http_major = 0;
+            parser->http_minor = 9;
+            UPDATE_STATE((ch == CR) ?
+              s_req_line_almost_done :
+              s_header_field_start);
+            CALLBACK_DATA(url);
+            break;
+          default:
+            UPDATE_STATE(parse_url_char(CURRENT_STATE(), ch));
+            if (UNLIKELY(CURRENT_STATE() == s_dead)) {
+              SET_ERRNO(HPE_INVALID_URL);
+              goto error;
+            }
+        }
+        break;
+      }
+
+      case s_req_http_start:
+        switch (ch) {
+          case 'H':
+            UPDATE_STATE(s_req_http_H);
+            break;
+          case ' ':
+            break;
+          default:
+            SET_ERRNO(HPE_INVALID_CONSTANT);
+            goto error;
+        }
+        break;
+
+      case s_req_http_H:
+        STRICT_CHECK(ch != 'T');
+        UPDATE_STATE(s_req_http_HT);
+        break;
+
+      case s_req_http_HT:
+        STRICT_CHECK(ch != 'T');
+        UPDATE_STATE(s_req_http_HTT);
+        break;
+
+      case s_req_http_HTT:
+        STRICT_CHECK(ch != 'P');
+        UPDATE_STATE(s_req_http_HTTP);
+        break;
+
+      case s_req_http_HTTP:
+        STRICT_CHECK(ch != '/');
+        UPDATE_STATE(s_req_http_major);
+        break;
+
+      case s_req_http_major:
+        if (UNLIKELY(!IS_NUM(ch))) {
+          SET_ERRNO(HPE_INVALID_VERSION);
+          goto error;
+        }
+
+        parser->http_major = ch - '0';
+        UPDATE_STATE(s_req_http_dot);
+        break;
+
+      case s_req_http_dot:
+      {
+        if (UNLIKELY(ch != '.')) {
+          SET_ERRNO(HPE_INVALID_VERSION);
+          goto error;
+        }
+
+        UPDATE_STATE(s_req_http_minor);
+        break;
+      }
+
+      case s_req_http_minor:
+        if (UNLIKELY(!IS_NUM(ch))) {
+          SET_ERRNO(HPE_INVALID_VERSION);
+          goto error;
+        }
+
+        parser->http_minor = ch - '0';
+        UPDATE_STATE(s_req_http_end);
+        break;
+
+      case s_req_http_end:
+      {
+        if (ch == CR) {
+          UPDATE_STATE(s_req_line_almost_done);
+          break;
+        }
+
+        if (ch == LF) {
+          UPDATE_STATE(s_header_field_start);
+          break;
+        }
+
+        SET_ERRNO(HPE_INVALID_VERSION);
+        goto error;
+        break;
+      }
+
+      /* end of request line */
+      case s_req_line_almost_done:
+      {
+        if (UNLIKELY(ch != LF)) {
+          SET_ERRNO(HPE_LF_EXPECTED);
+          goto error;
+        }
+
+        UPDATE_STATE(s_header_field_start);
+        break;
+      }
+
+      case s_header_field_start:
+      {
+        if (ch == CR) {
+          UPDATE_STATE(s_headers_almost_done);
+          break;
+        }
+
+        if (ch == LF) {
+          /* they might be just sending \n instead of \r\n so this would be
+           * the second \n to denote the end of headers*/
+          UPDATE_STATE(s_headers_almost_done);
+          REEXECUTE();
+        }
+
+        c = TOKEN(ch);
+
+        if (UNLIKELY(!c)) {
+          SET_ERRNO(HPE_INVALID_HEADER_TOKEN);
+          goto error;
+        }
+
+        MARK(header_field);
+
+        parser->index = 0;
+        UPDATE_STATE(s_header_field);
+
+        switch (c) {
+          case 'c':
+            parser->header_state = h_C;
+            break;
+
+          case 'p':
+            parser->header_state = h_matching_proxy_connection;
+            break;
+
+          case 't':
+            parser->header_state = h_matching_transfer_encoding;
+            break;
+
+          case 'u':
+            parser->header_state = h_matching_upgrade;
+            break;
+
+          default:
+            parser->header_state = h_general;
+            break;
+        }
+        break;
+      }
+
+      case s_header_field:
+      {
+        const char* start = p;
+        for (; p != data + len; p++) {
+          ch = *p;
+          c = TOKEN(ch);
+
+          if (!c)
+            break;
+
+          switch (parser->header_state) {
+            case h_general: {
+              size_t limit = data + len - p;
+              limit = MIN(limit, HTTP_MAX_HEADER_SIZE);
+              while (p+1 < data + limit && TOKEN(p[1])) {
+                p++;
+              }
+              break;
+            }
+
+            case h_C:
+              parser->index++;
+              parser->header_state = (c == 'o' ? h_CO : h_general);
+              break;
+
+            case h_CO:
+              parser->index++;
+              parser->header_state = (c == 'n' ? h_CON : h_general);
+              break;
+
+            case h_CON:
+              parser->index++;
+              switch (c) {
+                case 'n':
+                  parser->header_state = h_matching_connection;
+                  break;
+                case 't':
+                  parser->header_state = h_matching_content_length;
+                  break;
+                default:
+                  parser->header_state = h_general;
+                  break;
+              }
+              break;
+
+            /* connection */
+
+            case h_matching_connection:
+              parser->index++;
+              if (parser->index > sizeof(CONNECTION)-1
+                  || c != CONNECTION[parser->index]) {
+                parser->header_state = h_general;
+              } else if (parser->index == sizeof(CONNECTION)-2) {
+                parser->header_state = h_connection;
+              }
+              break;
+
+            /* proxy-connection */
+
+            case h_matching_proxy_connection:
+              parser->index++;
+              if (parser->index > sizeof(PROXY_CONNECTION)-1
+                  || c != PROXY_CONNECTION[parser->index]) {
+                parser->header_state = h_general;
+              } else if (parser->index == sizeof(PROXY_CONNECTION)-2) {
+                parser->header_state = h_connection;
+              }
+              break;
+
+            /* content-length */
+
+            case h_matching_content_length:
+              parser->index++;
+              if (parser->index > sizeof(CONTENT_LENGTH)-1
+                  || c != CONTENT_LENGTH[parser->index]) {
+                parser->header_state = h_general;
+              } else if (parser->index == sizeof(CONTENT_LENGTH)-2) {
+                parser->header_state = h_content_length;
+              }
+              break;
+
+            /* transfer-encoding */
+
+            case h_matching_transfer_encoding:
+              parser->index++;
+              if (parser->index > sizeof(TRANSFER_ENCODING)-1
+                  || c != TRANSFER_ENCODING[parser->index]) {
+                parser->header_state = h_general;
+              } else if (parser->index == sizeof(TRANSFER_ENCODING)-2) {
+                parser->header_state = h_transfer_encoding;
+              }
+              break;
+
+            /* upgrade */
+
+            case h_matching_upgrade:
+              parser->index++;
+              if (parser->index > sizeof(UPGRADE)-1
+                  || c != UPGRADE[parser->index]) {
+                parser->header_state = h_general;
+              } else if (parser->index == sizeof(UPGRADE)-2) {
+                parser->header_state = h_upgrade;
+              }
+              break;
+
+            case h_connection:
+            case h_content_length:
+            case h_transfer_encoding:
+            case h_upgrade:
+              if (ch != ' ') parser->header_state = h_general;
+              break;
+
+            default:
+              assert(0 && "Unknown header_state");
+              break;
+          }
+        }
+
+        if (p == data + len) {
+          --p;
+          COUNT_HEADER_SIZE(p - start);
+          break;
+        }
+
+        COUNT_HEADER_SIZE(p - start);
+
+        if (ch == ':') {
+          UPDATE_STATE(s_header_value_discard_ws);
+          CALLBACK_DATA(header_field);
+          break;
+        }
+
+        SET_ERRNO(HPE_INVALID_HEADER_TOKEN);
+        goto error;
+      }
+
+      case s_header_value_discard_ws:
+        if (ch == ' ' || ch == '\t') break;
+
+        if (ch == CR) {
+          UPDATE_STATE(s_header_value_discard_ws_almost_done);
+          break;
+        }
+
+        if (ch == LF) {
+          UPDATE_STATE(s_header_value_discard_lws);
+          break;
+        }
+
+        /* fall through */
+
+      case s_header_value_start:
+      {
+        MARK(header_value);
+
+        UPDATE_STATE(s_header_value);
+        parser->index = 0;
+
+        c = LOWER(ch);
+
+        switch (parser->header_state) {
+          case h_upgrade:
+            parser->flags |= F_UPGRADE;
+            parser->header_state = h_general;
+            break;
+
+          case h_transfer_encoding:
+            /* looking for 'Transfer-Encoding: chunked' */
+            if ('c' == c) {
+              parser->header_state = h_matching_transfer_encoding_chunked;
+            } else {
+              parser->header_state = h_general;
+            }
+            break;
+
+          case h_content_length:
+            if (UNLIKELY(!IS_NUM(ch))) {
+              SET_ERRNO(HPE_INVALID_CONTENT_LENGTH);
+              goto error;
+            }
+
+            if (parser->flags & F_CONTENTLENGTH) {
+              SET_ERRNO(HPE_UNEXPECTED_CONTENT_LENGTH);
+              goto error;
+            }
+
+            parser->flags |= F_CONTENTLENGTH;
+            parser->content_length = ch - '0';
+            parser->header_state = h_content_length_num;
+            break;
+
+          case h_connection:
+            /* looking for 'Connection: keep-alive' */
+            if (c == 'k') {
+              parser->header_state = h_matching_connection_keep_alive;
+            /* looking for 'Connection: close' */
+            } else if (c == 'c') {
+              parser->header_state = h_matching_connection_close;
+            } else if (c == 'u') {
+              parser->header_state = h_matching_connection_upgrade;
+            } else {
+              parser->header_state = h_matching_connection_token;
+            }
+            break;
+
+          /* Multi-value `Connection` header */
+          case h_matching_connection_token_start:
+            break;
+
+          default:
+            parser->header_state = h_general;
+            break;
+        }
+        break;
+      }
+
+      case s_header_value:
+      {
+        const char* start = p;
+        enum header_states h_state = (enum header_states) parser->header_state;
+        for (; p != data + len; p++) {
+          ch = *p;
+          if (ch == CR) {
+            UPDATE_STATE(s_header_almost_done);
+            parser->header_state = h_state;
+            CALLBACK_DATA(header_value);
+            break;
+          }
+
+          if (ch == LF) {
+            UPDATE_STATE(s_header_almost_done);
+            COUNT_HEADER_SIZE(p - start);
+            parser->header_state = h_state;
+            CALLBACK_DATA_NOADVANCE(header_value);
+            REEXECUTE();
+          }
+
+          if (!lenient && !IS_HEADER_CHAR(ch)) {
+            SET_ERRNO(HPE_INVALID_HEADER_TOKEN);
+            goto error;
+          }
+
+          c = LOWER(ch);
+
+          switch (h_state) {
+            case h_general:
+            {
+              const char* p_cr;
+              const char* p_lf;
+              size_t limit = data + len - p;
+
+              limit = MIN(limit, HTTP_MAX_HEADER_SIZE);
+
+              p_cr = (const char*) memchr(p, CR, limit);
+              p_lf = (const char*) memchr(p, LF, limit);
+              if (p_cr != NULL) {
+                if (p_lf != NULL && p_cr >= p_lf)
+                  p = p_lf;
+                else
+                  p = p_cr;
+              } else if (UNLIKELY(p_lf != NULL)) {
+                p = p_lf;
+              } else {
+                p = data + len;
+              }
+              --p;
+              break;
+            }
+
+            case h_connection:
+            case h_transfer_encoding:
+              assert(0 && "Shouldn't get here.");
+              break;
+
+            case h_content_length:
+              if (ch == ' ') break;
+              h_state = h_content_length_num;
+              /* fall through */
+
+            case h_content_length_num:
+            {
+              uint64_t t;
+
+              if (ch == ' ') {
+                h_state = h_content_length_ws;
+                break;
+              }
+
+              if (UNLIKELY(!IS_NUM(ch))) {
+                SET_ERRNO(HPE_INVALID_CONTENT_LENGTH);
+                parser->header_state = h_state;
+                goto error;
+              }
+
+              t = parser->content_length;
+              t *= 10;
+              t += ch - '0';
+
+              /* Overflow? Test against a conservative limit for simplicity. */
+              if (UNLIKELY((ULLONG_MAX - 10) / 10 < parser->content_length)) {
+                SET_ERRNO(HPE_INVALID_CONTENT_LENGTH);
+                parser->header_state = h_state;
+                goto error;
+              }
+
+              parser->content_length = t;
+              break;
+            }
+
+            case h_content_length_ws:
+              if (ch == ' ') break;
+              SET_ERRNO(HPE_INVALID_CONTENT_LENGTH);
+              parser->header_state = h_state;
+              goto error;
+
+            /* Transfer-Encoding: chunked */
+            case h_matching_transfer_encoding_chunked:
+              parser->index++;
+              if (parser->index > sizeof(CHUNKED)-1
+                  || c != CHUNKED[parser->index]) {
+                h_state = h_general;
+              } else if (parser->index == sizeof(CHUNKED)-2) {
+                h_state = h_transfer_encoding_chunked;
+              }
+              break;
+
+            case h_matching_connection_token_start:
+              /* looking for 'Connection: keep-alive' */
+              if (c == 'k') {
+                h_state = h_matching_connection_keep_alive;
+              /* looking for 'Connection: close' */
+              } else if (c == 'c') {
+                h_state = h_matching_connection_close;
+              } else if (c == 'u') {
+                h_state = h_matching_connection_upgrade;
+              } else if (STRICT_TOKEN(c)) {
+                h_state = h_matching_connection_token;
+              } else if (c == ' ' || c == '\t') {
+                /* Skip lws */
+              } else {
+                h_state = h_general;
+              }
+              break;
+
+            /* looking for 'Connection: keep-alive' */
+            case h_matching_connection_keep_alive:
+              parser->index++;
+              if (parser->index > sizeof(KEEP_ALIVE)-1
+                  || c != KEEP_ALIVE[parser->index]) {
+                h_state = h_matching_connection_token;
+              } else if (parser->index == sizeof(KEEP_ALIVE)-2) {
+                h_state = h_connection_keep_alive;
+              }
+              break;
+
+            /* looking for 'Connection: close' */
+            case h_matching_connection_close:
+              parser->index++;
+              if (parser->index > sizeof(CLOSE)-1 || c != CLOSE[parser->index]) {
+                h_state = h_matching_connection_token;
+              } else if (parser->index == sizeof(CLOSE)-2) {
+                h_state = h_connection_close;
+              }
+              break;
+
+            /* looking for 'Connection: upgrade' */
+            case h_matching_connection_upgrade:
+              parser->index++;
+              if (parser->index > sizeof(UPGRADE) - 1 ||
+                  c != UPGRADE[parser->index]) {
+                h_state = h_matching_connection_token;
+              } else if (parser->index == sizeof(UPGRADE)-2) {
+                h_state = h_connection_upgrade;
+              }
+              break;
+
+            case h_matching_connection_token:
+              if (ch == ',') {
+                h_state = h_matching_connection_token_start;
+                parser->index = 0;
+              }
+              break;
+
+            case h_transfer_encoding_chunked:
+              if (ch != ' ') h_state = h_general;
+              break;
+
+            case h_connection_keep_alive:
+            case h_connection_close:
+            case h_connection_upgrade:
+              if (ch == ',') {
+                if (h_state == h_connection_keep_alive) {
+                  parser->flags |= F_CONNECTION_KEEP_ALIVE;
+                } else if (h_state == h_connection_close) {
+                  parser->flags |= F_CONNECTION_CLOSE;
+                } else if (h_state == h_connection_upgrade) {
+                  parser->flags |= F_CONNECTION_UPGRADE;
+                }
+                h_state = h_matching_connection_token_start;
+                parser->index = 0;
+              } else if (ch != ' ') {
+                h_state = h_matching_connection_token;
+              }
+              break;
+
+            default:
+              UPDATE_STATE(s_header_value);
+              h_state = h_general;
+              break;
+          }
+        }
+        parser->header_state = h_state;
+
+        if (p == data + len)
+          --p;
+
+        COUNT_HEADER_SIZE(p - start);
+        break;
+      }
+
+      case s_header_almost_done:
+      {
+        if (UNLIKELY(ch != LF)) {
+          SET_ERRNO(HPE_LF_EXPECTED);
+          goto error;
+        }
+
+        UPDATE_STATE(s_header_value_lws);
+        break;
+      }
+
+      case s_header_value_lws:
+      {
+        if (ch == ' ' || ch == '\t') {
+          UPDATE_STATE(s_header_value_start);
+          REEXECUTE();
+        }
+
+        /* finished the header */
+        switch (parser->header_state) {
+          case h_connection_keep_alive:
+            parser->flags |= F_CONNECTION_KEEP_ALIVE;
+            break;
+          case h_connection_close:
+            parser->flags |= F_CONNECTION_CLOSE;
+            break;
+          case h_transfer_encoding_chunked:
+            parser->flags |= F_CHUNKED;
+            break;
+          case h_connection_upgrade:
+            parser->flags |= F_CONNECTION_UPGRADE;
+            break;
+          default:
+            break;
+        }
+
+        UPDATE_STATE(s_header_field_start);
+        REEXECUTE();
+      }
+
+      case s_header_value_discard_ws_almost_done:
+      {
+        STRICT_CHECK(ch != LF);
+        UPDATE_STATE(s_header_value_discard_lws);
+        break;
+      }
+
+      case s_header_value_discard_lws:
+      {
+        if (ch == ' ' || ch == '\t') {
+          UPDATE_STATE(s_header_value_discard_ws);
+          break;
+        } else {
+          switch (parser->header_state) {
+            case h_connection_keep_alive:
+              parser->flags |= F_CONNECTION_KEEP_ALIVE;
+              break;
+            case h_connection_close:
+              parser->flags |= F_CONNECTION_CLOSE;
+              break;
+            case h_connection_upgrade:
+              parser->flags |= F_CONNECTION_UPGRADE;
+              break;
+            case h_transfer_encoding_chunked:
+              parser->flags |= F_CHUNKED;
+              break;
+            default:
+              break;
+          }
+
+          /* header value was empty */
+          MARK(header_value);
+          UPDATE_STATE(s_header_field_start);
+          CALLBACK_DATA_NOADVANCE(header_value);
+          REEXECUTE();
+        }
+      }
+
+      case s_headers_almost_done:
+      {
+        STRICT_CHECK(ch != LF);
+
+        if (parser->flags & F_TRAILING) {
+          /* End of a chunked request */
+          UPDATE_STATE(s_message_done);
+          CALLBACK_NOTIFY_NOADVANCE(chunk_complete);
+          REEXECUTE();
+        }
+
+        /* Cannot use chunked encoding and a content-length header together
+           per the HTTP specification. */
+        if ((parser->flags & F_CHUNKED) &&
+            (parser->flags & F_CONTENTLENGTH)) {
+          SET_ERRNO(HPE_UNEXPECTED_CONTENT_LENGTH);
+          goto error;
+        }
+
+        UPDATE_STATE(s_headers_done);
+
+        /* Set this here so that on_headers_complete() callbacks can see it */
+        if ((parser->flags & F_UPGRADE) &&
+            (parser->flags & F_CONNECTION_UPGRADE)) {
+          /* For responses, "Upgrade: foo" and "Connection: upgrade" are
+           * mandatory only when it is a 101 Switching Protocols response,
+           * otherwise it is purely informational, to announce support.
+           */
+          parser->upgrade =
+              (parser->type == HTTP_REQUEST || parser->status_code == 101);
+        } else {
+          parser->upgrade = (parser->method == HTTP_CONNECT);
+        }
+
+        /* Here we call the headers_complete callback. This is somewhat
+         * different than other callbacks because if the user returns 1, we
+         * will interpret that as saying that this message has no body. This
+         * is needed for the annoying case of receiving a response to a HEAD
+         * request.
+         *
+         * We'd like to use CALLBACK_NOTIFY_NOADVANCE() here but we cannot, so
+         * we have to simulate it by handling a change in errno below.
+         */
+        if (settings->on_headers_complete) {
+          switch (settings->on_headers_complete(parser)) {
+            case 0:
+              break;
+
+            case 2:
+              parser->upgrade = 1;
+
+              /* fall through */
+            case 1:
+              parser->flags |= F_SKIPBODY;
+              break;
+
+            default:
+              SET_ERRNO(HPE_CB_headers_complete);
+              RETURN(p - data); /* Error */
+          }
+        }
+
+        if (HTTP_PARSER_ERRNO(parser) != HPE_OK) {
+          RETURN(p - data);
+        }
+
+        REEXECUTE();
+      }
+
+      case s_headers_done:
+      {
+        int hasBody;
+        STRICT_CHECK(ch != LF);
+
+        parser->nread = 0;
+        nread = 0;
+
+        hasBody = parser->flags & F_CHUNKED ||
+          (parser->content_length > 0 && parser->content_length != ULLONG_MAX);
+        if (parser->upgrade && (parser->method == HTTP_CONNECT ||
+                                (parser->flags & F_SKIPBODY) || !hasBody)) {
+          /* Exit, the rest of the message is in a different protocol. */
+          UPDATE_STATE(NEW_MESSAGE());
+          CALLBACK_NOTIFY(message_complete);
+          RETURN((p - data) + 1);
+        }
+
+        if (parser->flags & F_SKIPBODY) {
+          UPDATE_STATE(NEW_MESSAGE());
+          CALLBACK_NOTIFY(message_complete);
+        } else if (parser->flags & F_CHUNKED) {
+          /* chunked encoding - ignore Content-Length header */
+          UPDATE_STATE(s_chunk_size_start);
+        } else {
+          if (parser->content_length == 0) {
+            /* Content-Length header given but zero: Content-Length: 0\r\n */
+            UPDATE_STATE(NEW_MESSAGE());
+            CALLBACK_NOTIFY(message_complete);
+          } else if (parser->content_length != ULLONG_MAX) {
+            /* Content-Length header given and non-zero */
+            UPDATE_STATE(s_body_identity);
+          } else {
+            if (!http_message_needs_eof(parser)) {
+              /* Assume content-length 0 - read the next */
+              UPDATE_STATE(NEW_MESSAGE());
+              CALLBACK_NOTIFY(message_complete);
+            } else {
+              /* Read body until EOF */
+              UPDATE_STATE(s_body_identity_eof);
+            }
+          }
+        }
+
+        break;
+      }
+
+      case s_body_identity:
+      {
+        uint64_t to_read = MIN(parser->content_length,
+                               (uint64_t) ((data + len) - p));
+
+        assert(parser->content_length != 0
+            && parser->content_length != ULLONG_MAX);
+
+        /* The difference between advancing content_length and p is because
+         * the latter will automaticaly advance on the next loop iteration.
+         * Further, if content_length ends up at 0, we want to see the last
+         * byte again for our message complete callback.
+         */
+        MARK(body);
+        parser->content_length -= to_read;
+        p += to_read - 1;
+
+        if (parser->content_length == 0) {
+          UPDATE_STATE(s_message_done);
+
+          /* Mimic CALLBACK_DATA_NOADVANCE() but with one extra byte.
+           *
+           * The alternative to doing this is to wait for the next byte to
+           * trigger the data callback, just as in every other case. The
+           * problem with this is that this makes it difficult for the test
+           * harness to distinguish between complete-on-EOF and
+           * complete-on-length. It's not clear that this distinction is
+           * important for applications, but let's keep it for now.
+           */
+          CALLBACK_DATA_(body, p - body_mark + 1, p - data);
+          REEXECUTE();
+        }
+
+        break;
+      }
+
+      /* read until EOF */
+      case s_body_identity_eof:
+        MARK(body);
+        p = data + len - 1;
+
+        break;
+
+      case s_message_done:
+        UPDATE_STATE(NEW_MESSAGE());
+        CALLBACK_NOTIFY(message_complete);
+        if (parser->upgrade) {
+          /* Exit, the rest of the message is in a different protocol. */
+          RETURN((p - data) + 1);
+        }
+        break;
+
+      case s_chunk_size_start:
+      {
+        assert(nread == 1);
+        assert(parser->flags & F_CHUNKED);
+
+        unhex_val = unhex[(unsigned char)ch];
+        if (UNLIKELY(unhex_val == -1)) {
+          SET_ERRNO(HPE_INVALID_CHUNK_SIZE);
+          goto error;
+        }
+
+        parser->content_length = unhex_val;
+        UPDATE_STATE(s_chunk_size);
+        break;
+      }
+
+      case s_chunk_size:
+      {
+        uint64_t t;
+
+        assert(parser->flags & F_CHUNKED);
+
+        if (ch == CR) {
+          UPDATE_STATE(s_chunk_size_almost_done);
+          break;
+        }
+
+        unhex_val = unhex[(unsigned char)ch];
+
+        if (unhex_val == -1) {
+          if (ch == ';' || ch == ' ') {
+            UPDATE_STATE(s_chunk_parameters);
+            break;
+          }
+
+          SET_ERRNO(HPE_INVALID_CHUNK_SIZE);
+          goto error;
+        }
+
+        t = parser->content_length;
+        t *= 16;
+        t += unhex_val;
+
+        /* Overflow? Test against a conservative limit for simplicity. */
+        if (UNLIKELY((ULLONG_MAX - 16) / 16 < parser->content_length)) {
+          SET_ERRNO(HPE_INVALID_CONTENT_LENGTH);
+          goto error;
+        }
+
+        parser->content_length = t;
+        break;
+      }
+
+      case s_chunk_parameters:
+      {
+        assert(parser->flags & F_CHUNKED);
+        /* just ignore this shit. TODO check for overflow */
+        if (ch == CR) {
+          UPDATE_STATE(s_chunk_size_almost_done);
+          break;
+        }
+        break;
+      }
+
+      case s_chunk_size_almost_done:
+      {
+        assert(parser->flags & F_CHUNKED);
+        STRICT_CHECK(ch != LF);
+
+        parser->nread = 0;
+        nread = 0;
+
+        if (parser->content_length == 0) {
+          parser->flags |= F_TRAILING;
+          UPDATE_STATE(s_header_field_start);
+        } else {
+          UPDATE_STATE(s_chunk_data);
+        }
+        CALLBACK_NOTIFY(chunk_header);
+        break;
+      }
+
+      case s_chunk_data:
+      {
+        uint64_t to_read = MIN(parser->content_length,
+                               (uint64_t) ((data + len) - p));
+
+        assert(parser->flags & F_CHUNKED);
+        assert(parser->content_length != 0
+            && parser->content_length != ULLONG_MAX);
+
+        /* See the explanation in s_body_identity for why the content
+         * length and data pointers are managed this way.
+         */
+        MARK(body);
+        parser->content_length -= to_read;
+        p += to_read - 1;
+
+        if (parser->content_length == 0) {
+          UPDATE_STATE(s_chunk_data_almost_done);
+        }
+
+        break;
+      }
+
+      case s_chunk_data_almost_done:
+        assert(parser->flags & F_CHUNKED);
+        assert(parser->content_length == 0);
+        STRICT_CHECK(ch != CR);
+        UPDATE_STATE(s_chunk_data_done);
+        CALLBACK_DATA(body);
+        break;
+
+      case s_chunk_data_done:
+        assert(parser->flags & F_CHUNKED);
+        STRICT_CHECK(ch != LF);
+        parser->nread = 0;
+        nread = 0;
+        UPDATE_STATE(s_chunk_size_start);
+        CALLBACK_NOTIFY(chunk_complete);
+        break;
+
+      default:
+        assert(0 && "unhandled state");
+        SET_ERRNO(HPE_INVALID_INTERNAL_STATE);
+        goto error;
+    }
+  }
+
+  /* Run callbacks for any marks that we have leftover after we ran our of
+   * bytes. There should be at most one of these set, so it's OK to invoke
+   * them in series (unset marks will not result in callbacks).
+   *
+   * We use the NOADVANCE() variety of callbacks here because 'p' has already
+   * overflowed 'data' and this allows us to correct for the off-by-one that
+   * we'd otherwise have (since CALLBACK_DATA() is meant to be run with a 'p'
+   * value that's in-bounds).
+   */
+
+  assert(((header_field_mark ? 1 : 0) +
+          (header_value_mark ? 1 : 0) +
+          (url_mark ? 1 : 0)  +
+          (body_mark ? 1 : 0) +
+          (status_mark ? 1 : 0)) <= 1);
+
+  CALLBACK_DATA_NOADVANCE(header_field);
+  CALLBACK_DATA_NOADVANCE(header_value);
+  CALLBACK_DATA_NOADVANCE(url);
+  CALLBACK_DATA_NOADVANCE(body);
+  CALLBACK_DATA_NOADVANCE(status);
+
+  RETURN(len);
+
+error:
+  if (HTTP_PARSER_ERRNO(parser) == HPE_OK) {
+    SET_ERRNO(HPE_UNKNOWN);
+  }
+
+  RETURN(p - data);
+}
+
+
+/* Does the parser need to see an EOF to find the end of the message? */
+int
+http_message_needs_eof (const http_parser *parser)
+{
+  if (parser->type == HTTP_REQUEST) {
+    return 0;
+  }
+
+  /* See RFC 2616 section 4.4 */
+  if (parser->status_code / 100 == 1 || /* 1xx e.g. Continue */
+      parser->status_code == 204 ||     /* No Content */
+      parser->status_code == 304 ||     /* Not Modified */
+      parser->flags & F_SKIPBODY) {     /* response to a HEAD request */
+    return 0;
+  }
+
+  if ((parser->flags & F_CHUNKED) || parser->content_length != ULLONG_MAX) {
+    return 0;
+  }
+
+  return 1;
+}
+
+
+int
+http_should_keep_alive (const http_parser *parser)
+{
+  if (parser->http_major > 0 && parser->http_minor > 0) {
+    /* HTTP/1.1 */
+    if (parser->flags & F_CONNECTION_CLOSE) {
+      return 0;
+    }
+  } else {
+    /* HTTP/1.0 or earlier */
+    if (!(parser->flags & F_CONNECTION_KEEP_ALIVE)) {
+      return 0;
+    }
+  }
+
+  return !http_message_needs_eof(parser);
+}
+
+
+const char *
+http_method_str (enum http_method m)
+{
+  return ELEM_AT(method_strings, m, "<unknown>");
+}
+
+const char *
+http_status_str (enum http_status s)
+{
+  switch (s) {
+#define XX(num, name, string) case HTTP_STATUS_##name: return #string;
+    HTTP_STATUS_MAP(XX)
+#undef XX
+    default: return "<unknown>";
+  }
+}
+
+void
+http_parser_init (http_parser *parser, enum http_parser_type t)
+{
+  void *data = parser->data; /* preserve application data */
+  memset(parser, 0, sizeof(*parser));
+  parser->data = data;
+  parser->type = t;
+  parser->state = (t == HTTP_REQUEST ? s_start_req : (t == HTTP_RESPONSE ? s_start_res : s_start_req_or_res));
+  parser->http_errno = HPE_OK;
+}
+
+void
+http_parser_settings_init(http_parser_settings *settings)
+{
+  memset(settings, 0, sizeof(*settings));
+}
+
+const char *
+http_errno_name(enum http_errno err) {
+  assert(((size_t) err) < ARRAY_SIZE(http_strerror_tab));
+  return http_strerror_tab[err].name;
+}
+
+const char *
+http_errno_description(enum http_errno err) {
+  assert(((size_t) err) < ARRAY_SIZE(http_strerror_tab));
+  return http_strerror_tab[err].description;
+}
+
+static enum http_host_state
+http_parse_host_char(enum http_host_state s, const char ch) {
+  switch(s) {
+    case s_http_userinfo:
+    case s_http_userinfo_start:
+      if (ch == '@') {
+        return s_http_host_start;
+      }
+
+      if (IS_USERINFO_CHAR(ch)) {
+        return s_http_userinfo;
+      }
+      break;
+
+    case s_http_host_start:
+      if (ch == '[') {
+        return s_http_host_v6_start;
+      }
+
+      if (IS_HOST_CHAR(ch)) {
+        return s_http_host;
+      }
+
+      break;
+
+    case s_http_host:
+      if (IS_HOST_CHAR(ch)) {
+        return s_http_host;
+      }
+
+    /* fall through */
+    case s_http_host_v6_end:
+      if (ch == ':') {
+        return s_http_host_port_start;
+      }
+
+      break;
+
+    case s_http_host_v6:
+      if (ch == ']') {
+        return s_http_host_v6_end;
+      }
+
+    /* fall through */
+    case s_http_host_v6_start:
+      if (IS_HEX(ch) || ch == ':' || ch == '.') {
+        return s_http_host_v6;
+      }
+
+      if (s == s_http_host_v6 && ch == '%') {
+        return s_http_host_v6_zone_start;
+      }
+      break;
+
+    case s_http_host_v6_zone:
+      if (ch == ']') {
+        return s_http_host_v6_end;
+      }
+
+    /* fall through */
+    case s_http_host_v6_zone_start:
+      /* RFC 6874 Zone ID consists of 1*( unreserved / pct-encoded) */
+      if (IS_ALPHANUM(ch) || ch == '%' || ch == '.' || ch == '-' || ch == '_' ||
+          ch == '~') {
+        return s_http_host_v6_zone;
+      }
+      break;
+
+    case s_http_host_port:
+    case s_http_host_port_start:
+      if (IS_NUM(ch)) {
+        return s_http_host_port;
+      }
+
+      break;
+
+    default:
+      break;
+  }
+  return s_http_host_dead;
+}
+
+static int
+http_parse_host(const char * buf, struct http_parser_url *u, int found_at) {
+  enum http_host_state s;
+
+  const char *p;
+  size_t buflen = u->field_data[UF_HOST].off + u->field_data[UF_HOST].len;
+
+  assert(u->field_set & (1 << UF_HOST));
+
+  u->field_data[UF_HOST].len = 0;
+
+  s = found_at ? s_http_userinfo_start : s_http_host_start;
+
+  for (p = buf + u->field_data[UF_HOST].off; p < buf + buflen; p++) {
+    enum http_host_state new_s = http_parse_host_char(s, *p);
+
+    if (new_s == s_http_host_dead) {
+      return 1;
+    }
+
+    switch(new_s) {
+      case s_http_host:
+        if (s != s_http_host) {
+          u->field_data[UF_HOST].off = p - buf;
+        }
+        u->field_data[UF_HOST].len++;
+        break;
+
+      case s_http_host_v6:
+        if (s != s_http_host_v6) {
+          u->field_data[UF_HOST].off = p - buf;
+        }
+        u->field_data[UF_HOST].len++;
+        break;
+
+      case s_http_host_v6_zone_start:
+      case s_http_host_v6_zone:
+        u->field_data[UF_HOST].len++;
+        break;
+
+      case s_http_host_port:
+        if (s != s_http_host_port) {
+          u->field_data[UF_PORT].off = p - buf;
+          u->field_data[UF_PORT].len = 0;
+          u->field_set |= (1 << UF_PORT);
+        }
+        u->field_data[UF_PORT].len++;
+        break;
+
+      case s_http_userinfo:
+        if (s != s_http_userinfo) {
+          u->field_data[UF_USERINFO].off = p - buf ;
+          u->field_data[UF_USERINFO].len = 0;
+          u->field_set |= (1 << UF_USERINFO);
+        }
+        u->field_data[UF_USERINFO].len++;
+        break;
+
+      default:
+        break;
+    }
+    s = new_s;
+  }
+
+  /* Make sure we don't end somewhere unexpected */
+  switch (s) {
+    case s_http_host_start:
+    case s_http_host_v6_start:
+    case s_http_host_v6:
+    case s_http_host_v6_zone_start:
+    case s_http_host_v6_zone:
+    case s_http_host_port_start:
+    case s_http_userinfo:
+    case s_http_userinfo_start:
+      return 1;
+    default:
+      break;
+  }
+
+  return 0;
+}
+
+void
+http_parser_url_init(struct http_parser_url *u) {
+  memset(u, 0, sizeof(*u));
+}
+
+int
+http_parser_parse_url(const char *buf, size_t buflen, int is_connect,
+                      struct http_parser_url *u)
+{
+  enum state s;
+  const char *p;
+  enum http_parser_url_fields uf, old_uf;
+  int found_at = 0;
+
+  if (buflen == 0) {
+    return 1;
+  }
+
+  u->port = u->field_set = 0;
+  s = is_connect ? s_req_server_start : s_req_spaces_before_url;
+  old_uf = UF_MAX;
+
+  for (p = buf; p < buf + buflen; p++) {
+    s = parse_url_char(s, *p);
+
+    /* Figure out the next field that we're operating on */
+    switch (s) {
+      case s_dead:
+        return 1;
+
+      /* Skip delimeters */
+      case s_req_schema_slash:
+      case s_req_schema_slash_slash:
+      case s_req_server_start:
+      case s_req_query_string_start:
+      case s_req_fragment_start:
+        continue;
+
+      case s_req_schema:
+        uf = UF_SCHEMA;
+        break;
+
+      case s_req_server_with_at:
+        found_at = 1;
+
+      /* fall through */
+      case s_req_server:
+        uf = UF_HOST;
+        break;
+
+      case s_req_path:
+        uf = UF_PATH;
+        break;
+
+      case s_req_query_string:
+        uf = UF_QUERY;
+        break;
+
+      case s_req_fragment:
+        uf = UF_FRAGMENT;
+        break;
+
+      default:
+        assert(!"Unexpected state");
+        return 1;
+    }
+
+    /* Nothing's changed; soldier on */
+    if (uf == old_uf) {
+      u->field_data[uf].len++;
+      continue;
+    }
+
+    u->field_data[uf].off = p - buf;
+    u->field_data[uf].len = 1;
+
+    u->field_set |= (1 << uf);
+    old_uf = uf;
+  }
+
+  /* host must be present if there is a schema */
+  /* parsing http:///toto will fail */
+  if ((u->field_set & (1 << UF_SCHEMA)) &&
+      (u->field_set & (1 << UF_HOST)) == 0) {
+    return 1;
+  }
+
+  if (u->field_set & (1 << UF_HOST)) {
+    if (http_parse_host(buf, u, found_at) != 0) {
+      return 1;
+    }
+  }
+
+  /* CONNECT requests can only contain "hostname:port" */
+  if (is_connect && u->field_set != ((1 << UF_HOST)|(1 << UF_PORT))) {
+    return 1;
+  }
+
+  if (u->field_set & (1 << UF_PORT)) {
+    uint16_t off;
+    uint16_t len;
+    const char* p;
+    const char* end;
+    unsigned long v;
+
+    off = u->field_data[UF_PORT].off;
+    len = u->field_data[UF_PORT].len;
+    end = buf + off + len;
+
+    /* NOTE: The characters are already validated and are in the [0-9] range */
+    assert(off + len <= buflen && "Port number overflow");
+    v = 0;
+    for (p = buf + off; p < end; p++) {
+      v *= 10;
+      v += *p - '0';
+
+      /* Ports have a max value of 2^16 */
+      if (v > 0xffff) {
+        return 1;
+      }
+    }
+
+    u->port = (uint16_t) v;
+  }
+
+  return 0;
+}
+
+void
+http_parser_pause(http_parser *parser, int paused) {
+  /* Users should only be pausing/unpausing a parser that is not in an error
+   * state. In non-debug builds, there's not much that we can do about this
+   * other than ignore it.
+   */
+  if (HTTP_PARSER_ERRNO(parser) == HPE_OK ||
+      HTTP_PARSER_ERRNO(parser) == HPE_PAUSED) {
+    uint32_t nread = parser->nread; /* used by the SET_ERRNO macro */
+    SET_ERRNO((paused) ? HPE_PAUSED : HPE_OK);
+  } else {
+    assert(0 && "Attempting to pause parser in error state");
+  }
+}
+
+int
+http_body_is_final(const struct http_parser *parser) {
+    return parser->state == s_message_done;
+}
+
+unsigned long
+http_parser_version(void) {
+  return HTTP_PARSER_VERSION_MAJOR * 0x10000 |
+         HTTP_PARSER_VERSION_MINOR * 0x00100 |
+         HTTP_PARSER_VERSION_PATCH * 0x00001;
+}
+
+}  // namespace wpi
diff --git a/wpinet/src/main/native/cpp/jni/WPINetJNI.cpp b/wpinet/src/main/native/cpp/jni/WPINetJNI.cpp
new file mode 100644
index 0000000..1528001
--- /dev/null
+++ b/wpinet/src/main/native/cpp/jni/WPINetJNI.cpp
@@ -0,0 +1,335 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#include <jni.h>
+
+#include <wpi/jni_util.h>
+
+#include "../MulticastHandleManager.h"
+#include "edu_wpi_first_net_WPINetJNI.h"
+#include "wpinet/MulticastServiceAnnouncer.h"
+#include "wpinet/MulticastServiceResolver.h"
+#include "wpinet/PortForwarder.h"
+
+using namespace wpi::java;
+
+static JClass serviceDataCls;
+static JGlobal<jobjectArray> serviceDataEmptyArray;
+
+extern "C" {
+
+JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) {
+  JNIEnv* env;
+  if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
+    return JNI_ERR;
+  }
+
+  serviceDataCls = JClass{env, "edu/wpi/first/net/ServiceData"};
+  if (!serviceDataCls) {
+    return JNI_ERR;
+  }
+
+  serviceDataEmptyArray = JGlobal<jobjectArray>{
+      env, env->NewObjectArray(0, serviceDataCls, nullptr)};
+  if (serviceDataEmptyArray == nullptr) {
+    return JNI_ERR;
+  }
+
+  return JNI_VERSION_1_6;
+}
+
+JNIEXPORT void JNICALL JNI_OnUnload(JavaVM* vm, void* reserved) {
+  JNIEnv* env;
+  if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
+    return;
+  }
+
+  serviceDataEmptyArray.free(env);
+  serviceDataCls.free(env);
+}
+
+/*
+ * Class:     edu_wpi_first_net_WPINetJNI
+ * Method:    addPortForwarder
+ * Signature: (ILjava/lang/String;I)V
+ */
+JNIEXPORT void JNICALL
+Java_edu_wpi_first_net_WPINetJNI_addPortForwarder
+  (JNIEnv* env, jclass, jint port, jstring remoteHost, jint remotePort)
+{
+  wpi::PortForwarder::GetInstance().Add(static_cast<unsigned int>(port),
+                                        JStringRef{env, remoteHost}.str(),
+                                        static_cast<unsigned int>(remotePort));
+}
+
+/*
+ * Class:     edu_wpi_first_net_WPINetJNI
+ * Method:    removePortForwarder
+ * Signature: (I)V
+ */
+JNIEXPORT void JNICALL
+Java_edu_wpi_first_net_WPINetJNI_removePortForwarder
+  (JNIEnv* env, jclass, jint port)
+{
+  wpi::PortForwarder::GetInstance().Remove(port);
+}
+
+/*
+ * Class:     edu_wpi_first_net_WPINetJNI
+ * Method:    createMulticastServiceAnnouncer
+ * Signature: (Ljava/lang/String;Ljava/lang/String;I[Ljava/lang/Object;[Ljava/lang/Object;)I
+ */
+JNIEXPORT jint JNICALL
+Java_edu_wpi_first_net_WPINetJNI_createMulticastServiceAnnouncer
+  (JNIEnv* env, jclass, jstring serviceName, jstring serviceType, jint port,
+   jobjectArray keys, jobjectArray values)
+{
+  auto& manager = wpi::GetMulticastManager();
+  std::scoped_lock lock{manager.mutex};
+
+  JStringRef serviceNameRef{env, serviceName};
+  JStringRef serviceTypeRef{env, serviceType};
+
+  wpi::SmallVector<std::pair<std::string, std::string>, 8> txtVec;
+
+  if (keys != nullptr && values != nullptr) {
+    size_t keysLen = env->GetArrayLength(keys);
+
+    txtVec.reserve(keysLen);
+    for (size_t i = 0; i < keysLen; i++) {
+      JLocal<jstring> key{
+          env, static_cast<jstring>(env->GetObjectArrayElement(keys, i))};
+      JLocal<jstring> value{
+          env, static_cast<jstring>(env->GetObjectArrayElement(values, i))};
+
+      txtVec.emplace_back(std::pair<std::string, std::string>{
+          JStringRef{env, key}.str(), JStringRef{env, value}.str()});
+    }
+  }
+
+  auto announcer = std::make_unique<wpi::MulticastServiceAnnouncer>(
+      serviceNameRef.str(), serviceTypeRef.str(), port, txtVec);
+
+  size_t index = manager.handleIds.emplace_back(1);
+
+  manager.announcers[index] = std::move(announcer);
+
+  return static_cast<jint>(index);
+}
+
+/*
+ * Class:     edu_wpi_first_net_WPINetJNI
+ * Method:    freeMulticastServiceAnnouncer
+ * Signature: (I)V
+ */
+JNIEXPORT void JNICALL
+Java_edu_wpi_first_net_WPINetJNI_freeMulticastServiceAnnouncer
+  (JNIEnv* env, jclass, jint handle)
+{
+  auto& manager = wpi::GetMulticastManager();
+  std::scoped_lock lock{manager.mutex};
+  manager.announcers[handle] = nullptr;
+  manager.handleIds.erase(handle);
+}
+
+/*
+ * Class:     edu_wpi_first_net_WPINetJNI
+ * Method:    startMulticastServiceAnnouncer
+ * Signature: (I)V
+ */
+JNIEXPORT void JNICALL
+Java_edu_wpi_first_net_WPINetJNI_startMulticastServiceAnnouncer
+  (JNIEnv* env, jclass, jint handle)
+{
+  auto& manager = wpi::GetMulticastManager();
+  std::scoped_lock lock{manager.mutex};
+  auto& announcer = manager.announcers[handle];
+  announcer->Start();
+}
+
+/*
+ * Class:     edu_wpi_first_net_WPINetJNI
+ * Method:    stopMulticastServiceAnnouncer
+ * Signature: (I)V
+ */
+JNIEXPORT void JNICALL
+Java_edu_wpi_first_net_WPINetJNI_stopMulticastServiceAnnouncer
+  (JNIEnv* env, jclass, jint handle)
+{
+  auto& manager = wpi::GetMulticastManager();
+  std::scoped_lock lock{manager.mutex};
+  auto& announcer = manager.announcers[handle];
+  announcer->Stop();
+}
+
+/*
+ * Class:     edu_wpi_first_net_WPINetJNI
+ * Method:    getMulticastServiceAnnouncerHasImplementation
+ * Signature: (I)Z
+ */
+JNIEXPORT jboolean JNICALL
+Java_edu_wpi_first_net_WPINetJNI_getMulticastServiceAnnouncerHasImplementation
+  (JNIEnv* env, jclass, jint handle)
+{
+  auto& manager = wpi::GetMulticastManager();
+  std::scoped_lock lock{manager.mutex};
+  auto& announcer = manager.announcers[handle];
+  return announcer->HasImplementation();
+}
+
+/*
+ * Class:     edu_wpi_first_net_WPINetJNI
+ * Method:    createMulticastServiceResolver
+ * Signature: (Ljava/lang/String;)I
+ */
+JNIEXPORT jint JNICALL
+Java_edu_wpi_first_net_WPINetJNI_createMulticastServiceResolver
+  (JNIEnv* env, jclass, jstring serviceType)
+{
+  auto& manager = wpi::GetMulticastManager();
+  std::scoped_lock lock{manager.mutex};
+  JStringRef serviceTypeRef{env, serviceType};
+
+  auto resolver =
+      std::make_unique<wpi::MulticastServiceResolver>(serviceTypeRef.str());
+
+  size_t index = manager.handleIds.emplace_back(2);
+
+  manager.resolvers[index] = std::move(resolver);
+
+  return static_cast<jint>(index);
+}
+
+/*
+ * Class:     edu_wpi_first_net_WPINetJNI
+ * Method:    freeMulticastServiceResolver
+ * Signature: (I)V
+ */
+JNIEXPORT void JNICALL
+Java_edu_wpi_first_net_WPINetJNI_freeMulticastServiceResolver
+  (JNIEnv* env, jclass, jint handle)
+{
+  auto& manager = wpi::GetMulticastManager();
+  std::scoped_lock lock{manager.mutex};
+  manager.resolvers[handle] = nullptr;
+  manager.handleIds.erase(handle);
+}
+
+/*
+ * Class:     edu_wpi_first_net_WPINetJNI
+ * Method:    startMulticastServiceResolver
+ * Signature: (I)V
+ */
+JNIEXPORT void JNICALL
+Java_edu_wpi_first_net_WPINetJNI_startMulticastServiceResolver
+  (JNIEnv* env, jclass, jint handle)
+{
+  auto& manager = wpi::GetMulticastManager();
+  std::scoped_lock lock{manager.mutex};
+  auto& resolver = manager.resolvers[handle];
+  resolver->Start();
+}
+
+/*
+ * Class:     edu_wpi_first_net_WPINetJNI
+ * Method:    stopMulticastServiceResolver
+ * Signature: (I)V
+ */
+JNIEXPORT void JNICALL
+Java_edu_wpi_first_net_WPINetJNI_stopMulticastServiceResolver
+  (JNIEnv* env, jclass, jint handle)
+{
+  auto& manager = wpi::GetMulticastManager();
+  std::scoped_lock lock{manager.mutex};
+  auto& resolver = manager.resolvers[handle];
+  resolver->Stop();
+}
+
+/*
+ * Class:     edu_wpi_first_net_WPINetJNI
+ * Method:    getMulticastServiceResolverHasImplementation
+ * Signature: (I)Z
+ */
+JNIEXPORT jboolean JNICALL
+Java_edu_wpi_first_net_WPINetJNI_getMulticastServiceResolverHasImplementation
+  (JNIEnv* env, jclass, jint handle)
+{
+  auto& manager = wpi::GetMulticastManager();
+  std::scoped_lock lock{manager.mutex};
+  auto& resolver = manager.resolvers[handle];
+  return resolver->HasImplementation();
+}
+
+/*
+ * Class:     edu_wpi_first_net_WPINetJNI
+ * Method:    getMulticastServiceResolverEventHandle
+ * Signature: (I)I
+ */
+JNIEXPORT jint JNICALL
+Java_edu_wpi_first_net_WPINetJNI_getMulticastServiceResolverEventHandle
+  (JNIEnv* env, jclass, jint handle)
+{
+  auto& manager = wpi::GetMulticastManager();
+  std::scoped_lock lock{manager.mutex};
+  auto& resolver = manager.resolvers[handle];
+  return resolver->GetEventHandle();
+}
+
+/*
+ * Class:     edu_wpi_first_net_WPINetJNI
+ * Method:    getMulticastServiceResolverData
+ * Signature: (I)[Ljava/lang/Object;
+ */
+JNIEXPORT jobjectArray JNICALL
+Java_edu_wpi_first_net_WPINetJNI_getMulticastServiceResolverData
+  (JNIEnv* env, jclass, jint handle)
+{
+  static jmethodID constructor =
+      env->GetMethodID(serviceDataCls, "<init>",
+                       "(JILjava/lang/String;Ljava/lang/String;[Ljava/lang/"
+                       "String;[Ljava/lang/String;)V");
+  auto& manager = wpi::GetMulticastManager();
+  std::vector<wpi::MulticastServiceResolver::ServiceData> allData;
+  {
+    std::scoped_lock lock{manager.mutex};
+    auto& resolver = manager.resolvers[handle];
+    allData = resolver->GetData();
+  }
+  if (allData.empty()) {
+    return serviceDataEmptyArray;
+  }
+
+  JLocal<jobjectArray> returnData{
+      env, env->NewObjectArray(allData.size(), serviceDataCls, nullptr)};
+
+  for (auto&& data : allData) {
+    JLocal<jstring> serviceName{env, MakeJString(env, data.serviceName)};
+    JLocal<jstring> hostName{env, MakeJString(env, data.hostName)};
+
+    wpi::SmallVector<std::string_view, 8> keysRef;
+    wpi::SmallVector<std::string_view, 8> valuesRef;
+
+    size_t index = 0;
+    for (auto&& txt : data.txt) {
+      keysRef.emplace_back(txt.first);
+      valuesRef.emplace_back(txt.second);
+    }
+
+    JLocal<jobjectArray> keys{env, MakeJStringArray(env, keysRef)};
+    JLocal<jobjectArray> values{env, MakeJStringArray(env, valuesRef)};
+
+    JLocal<jobject> dataItem{
+        env, env->NewObject(serviceDataCls, constructor,
+                            static_cast<jlong>(data.ipv4Address),
+                            static_cast<jint>(data.port), serviceName.obj(),
+                            hostName.obj(), keys.obj(), values.obj())};
+
+    env->SetObjectArrayElement(returnData, index, dataItem);
+    index++;
+  }
+
+  return returnData;
+}
+
+}  // extern "C"
diff --git a/wpinet/src/main/native/cpp/raw_socket_istream.cpp b/wpinet/src/main/native/cpp/raw_socket_istream.cpp
new file mode 100644
index 0000000..b57d83d
--- /dev/null
+++ b/wpinet/src/main/native/cpp/raw_socket_istream.cpp
@@ -0,0 +1,33 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#include "wpinet/raw_socket_istream.h"
+
+#include "wpinet/NetworkStream.h"
+
+using namespace wpi;
+
+void raw_socket_istream::read_impl(void* data, size_t len) {
+  char* cdata = static_cast<char*>(data);
+  size_t pos = 0;
+
+  while (pos < len) {
+    NetworkStream::Error err;
+    size_t count = m_stream.receive(&cdata[pos], len - pos, &err, m_timeout);
+    if (count == 0) {
+      error_detected();
+      break;
+    }
+    pos += count;
+  }
+  set_read_count(pos);
+}
+
+void raw_socket_istream::close() {
+  m_stream.close();
+}
+
+size_t raw_socket_istream::in_avail() const {
+  return 0;
+}
diff --git a/wpinet/src/main/native/cpp/raw_socket_ostream.cpp b/wpinet/src/main/native/cpp/raw_socket_ostream.cpp
new file mode 100644
index 0000000..5eb0b2b
--- /dev/null
+++ b/wpinet/src/main/native/cpp/raw_socket_ostream.cpp
@@ -0,0 +1,42 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#include "wpinet/raw_socket_ostream.h"
+
+#include "wpinet/NetworkStream.h"
+
+using namespace wpi;
+
+raw_socket_ostream::~raw_socket_ostream() {
+  flush();
+  if (m_shouldClose) {
+    close();
+  }
+}
+
+void raw_socket_ostream::write_impl(const char* data, size_t len) {
+  size_t pos = 0;
+
+  while (pos < len) {
+    NetworkStream::Error err;
+    size_t count = m_stream.send(&data[pos], len - pos, &err);
+    if (count == 0) {
+      error_detected();
+      return;
+    }
+    pos += count;
+  }
+}
+
+uint64_t raw_socket_ostream::current_pos() const {
+  return 0;
+}
+
+void raw_socket_ostream::close() {
+  if (!m_shouldClose) {
+    return;
+  }
+  flush();
+  m_stream.close();
+}
diff --git a/wpinet/src/main/native/cpp/raw_uv_ostream.cpp b/wpinet/src/main/native/cpp/raw_uv_ostream.cpp
new file mode 100644
index 0000000..fdbd1b0
--- /dev/null
+++ b/wpinet/src/main/native/cpp/raw_uv_ostream.cpp
@@ -0,0 +1,39 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#include "wpinet/raw_uv_ostream.h"
+
+#include <cstring>
+
+using namespace wpi;
+
+void raw_uv_ostream::write_impl(const char* data, size_t len) {
+  while (len > 0) {
+    // allocate additional buffers as required
+    if (m_left == 0) {
+      m_bufs.emplace_back(m_alloc());
+      // we want bufs() to always be valid, so set len=0 and keep track of the
+      // amount of space remaining separately
+      m_left = m_bufs.back().len;
+      m_bufs.back().len = 0;
+      assert(m_left != 0);
+    }
+
+    size_t amt = (std::min)(m_left, len);
+    auto& buf = m_bufs.back();
+    std::memcpy(buf.base + buf.len, data, amt);
+    data += amt;
+    len -= amt;
+    buf.len += amt;
+    m_left -= amt;
+  }
+}
+
+uint64_t raw_uv_ostream::current_pos() const {
+  uint64_t size = 0;
+  for (auto&& buf : m_bufs) {
+    size += buf.len;
+  }
+  return size;
+}
diff --git a/wpinet/src/main/native/cpp/uv/Async.cpp b/wpinet/src/main/native/cpp/uv/Async.cpp
new file mode 100644
index 0000000..f84bb9b
--- /dev/null
+++ b/wpinet/src/main/native/cpp/uv/Async.cpp
@@ -0,0 +1,33 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#include "wpinet/uv/Async.h"
+
+#include "wpinet/uv/Loop.h"
+
+namespace wpi::uv {
+
+Async<>::~Async() noexcept {
+  if (auto loop = m_loop.lock()) {
+    Close();
+  } else {
+    ForceClosed();
+  }
+}
+
+std::shared_ptr<Async<>> Async<>::Create(const std::shared_ptr<Loop>& loop) {
+  auto h = std::make_shared<Async>(loop, private_init{});
+  int err = uv_async_init(loop->GetRaw(), h->GetRaw(), [](uv_async_t* handle) {
+    Async& h = *static_cast<Async*>(handle->data);
+    h.wakeup();
+  });
+  if (err < 0) {
+    loop->ReportError(err);
+    return nullptr;
+  }
+  h->Keep();
+  return h;
+}
+
+}  // namespace wpi::uv
diff --git a/wpinet/src/main/native/cpp/uv/Check.cpp b/wpinet/src/main/native/cpp/uv/Check.cpp
new file mode 100644
index 0000000..13c2229
--- /dev/null
+++ b/wpinet/src/main/native/cpp/uv/Check.cpp
@@ -0,0 +1,29 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#include "wpinet/uv/Check.h"
+
+#include "wpinet/uv/Loop.h"
+
+namespace wpi::uv {
+
+std::shared_ptr<Check> Check::Create(Loop& loop) {
+  auto h = std::make_shared<Check>(private_init{});
+  int err = uv_check_init(loop.GetRaw(), h->GetRaw());
+  if (err < 0) {
+    loop.ReportError(err);
+    return nullptr;
+  }
+  h->Keep();
+  return h;
+}
+
+void Check::Start() {
+  Invoke(&uv_check_start, GetRaw(), [](uv_check_t* handle) {
+    Check& h = *static_cast<Check*>(handle->data);
+    h.check();
+  });
+}
+
+}  // namespace wpi::uv
diff --git a/wpinet/src/main/native/cpp/uv/FsEvent.cpp b/wpinet/src/main/native/cpp/uv/FsEvent.cpp
new file mode 100644
index 0000000..044390e
--- /dev/null
+++ b/wpinet/src/main/native/cpp/uv/FsEvent.cpp
@@ -0,0 +1,64 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#include "wpinet/uv/FsEvent.h"
+
+#include <cstdlib>
+
+#include <wpi/SmallString.h>
+
+#include "wpinet/uv/Loop.h"
+
+namespace wpi::uv {
+
+std::shared_ptr<FsEvent> FsEvent::Create(Loop& loop) {
+  auto h = std::make_shared<FsEvent>(private_init{});
+  int err = uv_fs_event_init(loop.GetRaw(), h->GetRaw());
+  if (err < 0) {
+    loop.ReportError(err);
+    return nullptr;
+  }
+  h->Keep();
+  return h;
+}
+
+void FsEvent::Start(std::string_view path, unsigned int flags) {
+  SmallString<128> pathBuf{path};
+  Invoke(
+      &uv_fs_event_start, GetRaw(),
+      [](uv_fs_event_t* handle, const char* filename, int events, int status) {
+        FsEvent& h = *static_cast<FsEvent*>(handle->data);
+        if (status < 0) {
+          h.ReportError(status);
+        } else {
+          h.fsEvent(filename, events);
+        }
+      },
+      pathBuf.c_str(), flags);
+}
+
+std::string FsEvent::GetPath() {
+  // Per the libuv docs, GetPath() always gives us a null-terminated string.
+  // common case should be small
+  char buf[128];
+  size_t size = 128;
+  int r = uv_fs_event_getpath(GetRaw(), buf, &size);
+  if (r == 0) {
+    return buf;
+  } else if (r == UV_ENOBUFS) {
+    // need to allocate a big enough buffer
+    char* buf2 = static_cast<char*>(std::malloc(size));
+    r = uv_fs_event_getpath(GetRaw(), buf2, &size);
+    if (r == 0) {
+      std::string out{buf2};
+      std::free(buf2);
+      return out;
+    }
+    std::free(buf2);
+  }
+  ReportError(r);
+  return std::string{};
+}
+
+}  // namespace wpi::uv
diff --git a/wpinet/src/main/native/cpp/uv/GetAddrInfo.cpp b/wpinet/src/main/native/cpp/uv/GetAddrInfo.cpp
new file mode 100644
index 0000000..14721f2
--- /dev/null
+++ b/wpinet/src/main/native/cpp/uv/GetAddrInfo.cpp
@@ -0,0 +1,52 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#include "wpinet/uv/GetAddrInfo.h"
+
+#include <wpi/SmallString.h>
+
+#include "wpinet/uv/Loop.h"
+#include "wpinet/uv/util.h"
+
+namespace wpi::uv {
+
+GetAddrInfoReq::GetAddrInfoReq() {
+  error = [this](Error err) { GetLoop().error(err); };
+}
+
+void GetAddrInfo(Loop& loop, const std::shared_ptr<GetAddrInfoReq>& req,
+                 std::string_view node, std::string_view service,
+                 const addrinfo* hints) {
+  SmallString<128> nodeStr{node};
+  SmallString<128> serviceStr{service};
+  int err = uv_getaddrinfo(
+      loop.GetRaw(), req->GetRaw(),
+      [](uv_getaddrinfo_t* req, int status, addrinfo* res) {
+        auto& h = *static_cast<GetAddrInfoReq*>(req->data);
+        if (status < 0) {
+          h.ReportError(status);
+        } else {
+          h.resolved(*res);
+        }
+        uv_freeaddrinfo(res);
+        h.Release();  // this is always a one-shot
+      },
+      node.empty() ? nullptr : nodeStr.c_str(),
+      service.empty() ? nullptr : serviceStr.c_str(), hints);
+  if (err < 0) {
+    loop.ReportError(err);
+  } else {
+    req->Keep();
+  }
+}
+
+void GetAddrInfo(Loop& loop, std::function<void(const addrinfo&)> callback,
+                 std::string_view node, std::string_view service,
+                 const addrinfo* hints) {
+  auto req = std::make_shared<GetAddrInfoReq>();
+  req->resolved.connect(std::move(callback));
+  GetAddrInfo(loop, req, node, service, hints);
+}
+
+}  // namespace wpi::uv
diff --git a/wpinet/src/main/native/cpp/uv/GetNameInfo.cpp b/wpinet/src/main/native/cpp/uv/GetNameInfo.cpp
new file mode 100644
index 0000000..a6ad36d
--- /dev/null
+++ b/wpinet/src/main/native/cpp/uv/GetNameInfo.cpp
@@ -0,0 +1,94 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#include "wpinet/uv/GetNameInfo.h"
+
+#include "wpinet/uv/Loop.h"
+#include "wpinet/uv/util.h"
+
+namespace wpi::uv {
+
+GetNameInfoReq::GetNameInfoReq() {
+  error = [this](Error err) { GetLoop().error(err); };
+}
+
+void GetNameInfo(Loop& loop, const std::shared_ptr<GetNameInfoReq>& req,
+                 const sockaddr& addr, int flags) {
+  int err = uv_getnameinfo(
+      loop.GetRaw(), req->GetRaw(),
+      [](uv_getnameinfo_t* req, int status, const char* hostname,
+         const char* service) {
+        auto& h = *static_cast<GetNameInfoReq*>(req->data);
+        if (status < 0) {
+          h.ReportError(status);
+        } else {
+          h.resolved(hostname, service);
+        }
+        h.Release();  // this is always a one-shot
+      },
+      &addr, flags);
+  if (err < 0) {
+    loop.ReportError(err);
+  } else {
+    req->Keep();
+  }
+}
+
+void GetNameInfo(Loop& loop,
+                 std::function<void(const char*, const char*)> callback,
+                 const sockaddr& addr, int flags) {
+  auto req = std::make_shared<GetNameInfoReq>();
+  req->resolved.connect(std::move(callback));
+  GetNameInfo(loop, req, addr, flags);
+}
+
+void GetNameInfo4(Loop& loop, const std::shared_ptr<GetNameInfoReq>& req,
+                  std::string_view ip, unsigned int port, int flags) {
+  sockaddr_in addr;
+  int err = NameToAddr(ip, port, &addr);
+  if (err < 0) {
+    loop.ReportError(err);
+  } else {
+    GetNameInfo(loop, req, reinterpret_cast<const sockaddr&>(addr), flags);
+  }
+}
+
+void GetNameInfo4(Loop& loop,
+                  std::function<void(const char*, const char*)> callback,
+                  std::string_view ip, unsigned int port, int flags) {
+  sockaddr_in addr;
+  int err = NameToAddr(ip, port, &addr);
+  if (err < 0) {
+    loop.ReportError(err);
+  } else {
+    GetNameInfo(loop, std::move(callback),
+                reinterpret_cast<const sockaddr&>(addr), flags);
+  }
+}
+
+void GetNameInfo6(Loop& loop, const std::shared_ptr<GetNameInfoReq>& req,
+                  std::string_view ip, unsigned int port, int flags) {
+  sockaddr_in6 addr;
+  int err = NameToAddr(ip, port, &addr);
+  if (err < 0) {
+    loop.ReportError(err);
+  } else {
+    GetNameInfo(loop, req, reinterpret_cast<const sockaddr&>(addr), flags);
+  }
+}
+
+void GetNameInfo6(Loop& loop,
+                  std::function<void(const char*, const char*)> callback,
+                  std::string_view ip, unsigned int port, int flags) {
+  sockaddr_in6 addr;
+  int err = NameToAddr(ip, port, &addr);
+  if (err < 0) {
+    loop.ReportError(err);
+  } else {
+    GetNameInfo(loop, std::move(callback),
+                reinterpret_cast<const sockaddr&>(addr), flags);
+  }
+}
+
+}  // namespace wpi::uv
diff --git a/wpinet/src/main/native/cpp/uv/Handle.cpp b/wpinet/src/main/native/cpp/uv/Handle.cpp
new file mode 100644
index 0000000..04af133
--- /dev/null
+++ b/wpinet/src/main/native/cpp/uv/Handle.cpp
@@ -0,0 +1,35 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#include "wpinet/uv/Handle.h"
+
+using namespace wpi::uv;
+
+Handle::~Handle() noexcept {
+  if (!m_closed && m_uv_handle->type != UV_UNKNOWN_HANDLE) {
+    uv_close(m_uv_handle, [](uv_handle_t* uv_handle) { std::free(uv_handle); });
+  } else {
+    std::free(m_uv_handle);
+  }
+}
+
+void Handle::Close() noexcept {
+  if (!IsClosing()) {
+    uv_close(m_uv_handle, [](uv_handle_t* handle) {
+      Handle& h = *static_cast<Handle*>(handle->data);
+      h.closed();
+      h.Release();  // free ourselves
+    });
+    m_closed = true;
+  }
+}
+
+void Handle::AllocBuf(uv_handle_t* handle, size_t size, uv_buf_t* buf) {
+  auto& h = *static_cast<Handle*>(handle->data);
+  *buf = h.m_allocBuf(size);
+}
+
+void Handle::DefaultFreeBuf(Buffer& buf) {
+  buf.Deallocate();
+}
diff --git a/wpinet/src/main/native/cpp/uv/Idle.cpp b/wpinet/src/main/native/cpp/uv/Idle.cpp
new file mode 100644
index 0000000..452bc7e
--- /dev/null
+++ b/wpinet/src/main/native/cpp/uv/Idle.cpp
@@ -0,0 +1,29 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#include "wpinet/uv/Idle.h"
+
+#include "wpinet/uv/Loop.h"
+
+namespace wpi::uv {
+
+std::shared_ptr<Idle> Idle::Create(Loop& loop) {
+  auto h = std::make_shared<Idle>(private_init{});
+  int err = uv_idle_init(loop.GetRaw(), h->GetRaw());
+  if (err < 0) {
+    loop.ReportError(err);
+    return nullptr;
+  }
+  h->Keep();
+  return h;
+}
+
+void Idle::Start() {
+  Invoke(&uv_idle_start, GetRaw(), [](uv_idle_t* handle) {
+    Idle& h = *static_cast<Idle*>(handle->data);
+    h.idle();
+  });
+}
+
+}  // namespace wpi::uv
diff --git a/wpinet/src/main/native/cpp/uv/Loop.cpp b/wpinet/src/main/native/cpp/uv/Loop.cpp
new file mode 100644
index 0000000..d48e230
--- /dev/null
+++ b/wpinet/src/main/native/cpp/uv/Loop.cpp
@@ -0,0 +1,70 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#include "wpinet/uv/Loop.h"
+
+using namespace wpi::uv;
+
+Loop::Loop(const private_init&) noexcept {
+#ifndef _WIN32
+  // Ignore SIGPIPE (see https://github.com/joyent/libuv/issues/1254)
+  static bool once = []() {
+    signal(SIGPIPE, SIG_IGN);
+    return true;
+  }();
+  (void)once;
+#endif
+}
+
+Loop::~Loop() noexcept {
+  if (m_loop) {
+    m_loop->data = nullptr;
+    Close();
+  }
+}
+
+std::shared_ptr<Loop> Loop::Create() {
+  auto loop = std::make_shared<Loop>(private_init{});
+  if (uv_loop_init(&loop->m_loopStruct) < 0) {
+    return nullptr;
+  }
+  loop->m_loop = &loop->m_loopStruct;
+  loop->m_loop->data = loop.get();
+  return loop;
+}
+
+std::shared_ptr<Loop> Loop::GetDefault() {
+  static std::shared_ptr<Loop> loop = std::make_shared<Loop>(private_init{});
+  loop->m_loop = uv_default_loop();
+  if (!loop->m_loop) {
+    return nullptr;
+  }
+  loop->m_loop->data = loop.get();
+  return loop;
+}
+
+void Loop::Close() {
+  int err = uv_loop_close(m_loop);
+  if (err < 0) {
+    ReportError(err);
+  }
+}
+
+void Loop::Walk(function_ref<void(Handle&)> callback) {
+  uv_walk(
+      m_loop,
+      [](uv_handle_t* handle, void* func) {
+        auto& h = *static_cast<Handle*>(handle->data);
+        auto& f = *static_cast<function_ref<void(Handle&)>*>(func);
+        f(h);
+      },
+      &callback);
+}
+
+void Loop::Fork() {
+  int err = uv_loop_fork(m_loop);
+  if (err < 0) {
+    ReportError(err);
+  }
+}
diff --git a/wpinet/src/main/native/cpp/uv/NameToAddr.cpp b/wpinet/src/main/native/cpp/uv/NameToAddr.cpp
new file mode 100644
index 0000000..2f3a003
--- /dev/null
+++ b/wpinet/src/main/native/cpp/uv/NameToAddr.cpp
@@ -0,0 +1,59 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#include "wpinet/uv/util.h"  // NOLINT(build/include_order)
+
+#include <cstring>
+
+#include <wpi/SmallString.h>
+
+namespace wpi::uv {
+
+int NameToAddr(std::string_view ip, unsigned int port, sockaddr_in* addr) {
+  if (ip.empty()) {
+    std::memset(addr, 0, sizeof(sockaddr_in));
+    addr->sin_family = PF_INET;
+    addr->sin_addr.s_addr = INADDR_ANY;
+    addr->sin_port = htons(port);
+    return 0;
+  } else {
+    SmallString<128> ipBuf{ip};
+    return uv_ip4_addr(ipBuf.c_str(), port, addr);
+  }
+}
+
+int NameToAddr(std::string_view ip, unsigned int port, sockaddr_in6* addr) {
+  if (ip.empty()) {
+    std::memset(addr, 0, sizeof(sockaddr_in6));
+    addr->sin6_family = PF_INET6;
+    addr->sin6_addr = in6addr_any;
+    addr->sin6_port = htons(port);
+    return 0;
+  } else {
+    SmallString<128> ipBuf{ip};
+    return uv_ip6_addr(ipBuf.c_str(), port, addr);
+  }
+}
+
+int NameToAddr(std::string_view ip, in_addr* addr) {
+  if (ip.empty()) {
+    addr->s_addr = INADDR_ANY;
+    return 0;
+  } else {
+    SmallString<128> ipBuf{ip};
+    return uv_inet_pton(AF_INET, ipBuf.c_str(), addr);
+  }
+}
+
+int NameToAddr(std::string_view ip, in6_addr* addr) {
+  if (ip.empty()) {
+    *addr = in6addr_any;
+    return 0;
+  } else {
+    SmallString<128> ipBuf{ip};
+    return uv_inet_pton(AF_INET6, ipBuf.c_str(), addr);
+  }
+}
+
+}  // namespace wpi::uv
diff --git a/wpinet/src/main/native/cpp/uv/NetworkStream.cpp b/wpinet/src/main/native/cpp/uv/NetworkStream.cpp
new file mode 100644
index 0000000..3538596
--- /dev/null
+++ b/wpinet/src/main/native/cpp/uv/NetworkStream.cpp
@@ -0,0 +1,30 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#include "wpinet/uv/NetworkStream.h"
+
+namespace wpi::uv {
+
+ConnectReq::ConnectReq() {
+  error = [this](Error err) { GetStream().error(err); };
+}
+
+void NetworkStream::Listen(int backlog) {
+  Invoke(&uv_listen, GetRawStream(), backlog,
+         [](uv_stream_t* handle, int status) {
+           auto& h = *static_cast<NetworkStream*>(handle->data);
+           if (status < 0) {
+             h.ReportError(status);
+           } else {
+             h.connection();
+           }
+         });
+}
+
+void NetworkStream::Listen(std::function<void()> callback, int backlog) {
+  connection.connect(std::move(callback));
+  Listen(backlog);
+}
+
+}  // namespace wpi::uv
diff --git a/wpinet/src/main/native/cpp/uv/Pipe.cpp b/wpinet/src/main/native/cpp/uv/Pipe.cpp
new file mode 100644
index 0000000..9548874
--- /dev/null
+++ b/wpinet/src/main/native/cpp/uv/Pipe.cpp
@@ -0,0 +1,138 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#include "wpinet/uv/Pipe.h"
+
+#include <cstdlib>
+
+#include <wpi/SmallString.h>
+
+namespace wpi::uv {
+
+std::shared_ptr<Pipe> Pipe::Create(Loop& loop, bool ipc) {
+  auto h = std::make_shared<Pipe>(private_init{});
+  int err = uv_pipe_init(loop.GetRaw(), h->GetRaw(), ipc ? 1 : 0);
+  if (err < 0) {
+    loop.ReportError(err);
+    return nullptr;
+  }
+  h->Keep();
+  return h;
+}
+
+void Pipe::Reuse(std::function<void()> callback, bool ipc) {
+  if (IsClosing()) {
+    return;
+  }
+  if (!m_reuseData) {
+    m_reuseData = std::make_unique<ReuseData>();
+  }
+  m_reuseData->callback = std::move(callback);
+  m_reuseData->ipc = ipc;
+  uv_close(GetRawHandle(), [](uv_handle_t* handle) {
+    Pipe& h = *static_cast<Pipe*>(handle->data);
+    if (!h.m_reuseData) {
+      return;
+    }
+    auto data = std::move(h.m_reuseData);
+    auto err =
+        uv_pipe_init(h.GetLoopRef().GetRaw(), h.GetRaw(), data->ipc ? 1 : 0);
+    if (err < 0) {
+      h.ReportError(err);
+      return;
+    }
+    data->callback();
+  });
+}
+
+std::shared_ptr<Pipe> Pipe::Accept() {
+  auto client = Create(GetLoopRef(), GetRaw()->ipc);
+  if (!client) {
+    return nullptr;
+  }
+  if (!Accept(client)) {
+    client->Release();
+    return nullptr;
+  }
+  return client;
+}
+
+Pipe* Pipe::DoAccept() {
+  return Accept().get();
+}
+
+void Pipe::Bind(std::string_view name) {
+  SmallString<128> nameBuf{name};
+  Invoke(&uv_pipe_bind, GetRaw(), nameBuf.c_str());
+}
+
+void Pipe::Connect(std::string_view name,
+                   const std::shared_ptr<PipeConnectReq>& req) {
+  SmallString<128> nameBuf{name};
+  uv_pipe_connect(req->GetRaw(), GetRaw(), nameBuf.c_str(),
+                  [](uv_connect_t* req, int status) {
+                    auto& h = *static_cast<PipeConnectReq*>(req->data);
+                    if (status < 0) {
+                      h.ReportError(status);
+                    } else {
+                      h.connected();
+                    }
+                    h.Release();  // this is always a one-shot
+                  });
+  req->Keep();
+}
+
+void Pipe::Connect(std::string_view name, std::function<void()> callback) {
+  auto req = std::make_shared<PipeConnectReq>();
+  req->connected.connect(std::move(callback));
+  Connect(name, req);
+}
+
+std::string Pipe::GetSock() {
+  // Per libuv docs, the returned buffer is NOT null terminated.
+  // common case should be small
+  char buf[128];
+  size_t size = 128;
+  int r = uv_pipe_getsockname(GetRaw(), buf, &size);
+  if (r == 0) {
+    return std::string{buf, size};
+  } else if (r == UV_ENOBUFS) {
+    // need to allocate a big enough buffer
+    char* buf2 = static_cast<char*>(std::malloc(size));
+    r = uv_pipe_getsockname(GetRaw(), buf2, &size);
+    if (r == 0) {
+      std::string out{buf2, size};
+      std::free(buf2);
+      return out;
+    }
+    std::free(buf2);
+  }
+  ReportError(r);
+  return std::string{};
+}
+
+std::string Pipe::GetPeer() {
+  // Per libuv docs, the returned buffer is NOT null terminated.
+  // common case should be small
+  char buf[128];
+  size_t size = 128;
+  int r = uv_pipe_getpeername(GetRaw(), buf, &size);
+  if (r == 0) {
+    return std::string{buf, size};
+  } else if (r == UV_ENOBUFS) {
+    // need to allocate a big enough buffer
+    char* buf2 = static_cast<char*>(std::malloc(size));
+    r = uv_pipe_getpeername(GetRaw(), buf2, &size);
+    if (r == 0) {
+      std::string out{buf2, size};
+      std::free(buf2);
+      return out;
+    }
+    std::free(buf2);
+  }
+  ReportError(r);
+  return std::string{};
+}
+
+}  // namespace wpi::uv
diff --git a/wpinet/src/main/native/cpp/uv/Poll.cpp b/wpinet/src/main/native/cpp/uv/Poll.cpp
new file mode 100644
index 0000000..3713453
--- /dev/null
+++ b/wpinet/src/main/native/cpp/uv/Poll.cpp
@@ -0,0 +1,95 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#include "wpinet/uv/Poll.h"
+
+#include "wpinet/uv/Loop.h"
+
+namespace wpi::uv {
+
+std::shared_ptr<Poll> Poll::Create(Loop& loop, int fd) {
+  auto h = std::make_shared<Poll>(private_init{});
+  int err = uv_poll_init(loop.GetRaw(), h->GetRaw(), fd);
+  if (err < 0) {
+    loop.ReportError(err);
+    return nullptr;
+  }
+  h->Keep();
+  return h;
+}
+
+std::shared_ptr<Poll> Poll::CreateSocket(Loop& loop, uv_os_sock_t sock) {
+  auto h = std::make_shared<Poll>(private_init{});
+  int err = uv_poll_init_socket(loop.GetRaw(), h->GetRaw(), sock);
+  if (err < 0) {
+    loop.ReportError(err);
+    return nullptr;
+  }
+  h->Keep();
+  return h;
+}
+
+void Poll::Reuse(int fd, std::function<void()> callback) {
+  if (IsClosing()) {
+    return;
+  }
+  if (!m_reuseData) {
+    m_reuseData = std::make_unique<ReuseData>();
+  }
+  m_reuseData->callback = std::move(callback);
+  m_reuseData->isSocket = false;
+  m_reuseData->fd = fd;
+  uv_close(GetRawHandle(), [](uv_handle_t* handle) {
+    Poll& h = *static_cast<Poll*>(handle->data);
+    if (!h.m_reuseData || h.m_reuseData->isSocket) {
+      return;  // just in case
+    }
+    auto data = std::move(h.m_reuseData);
+    int err = uv_poll_init(h.GetLoopRef().GetRaw(), h.GetRaw(), data->fd);
+    if (err < 0) {
+      h.ReportError(err);
+      return;
+    }
+    data->callback();
+  });
+}
+
+void Poll::ReuseSocket(uv_os_sock_t sock, std::function<void()> callback) {
+  if (IsClosing()) {
+    return;
+  }
+  if (!m_reuseData) {
+    m_reuseData = std::make_unique<ReuseData>();
+  }
+  m_reuseData->callback = std::move(callback);
+  m_reuseData->isSocket = true;
+  m_reuseData->sock = sock;
+  uv_close(GetRawHandle(), [](uv_handle_t* handle) {
+    Poll& h = *static_cast<Poll*>(handle->data);
+    if (!h.m_reuseData || !h.m_reuseData->isSocket) {
+      return;  // just in case
+    }
+    auto data = std::move(h.m_reuseData);
+    int err = uv_poll_init(h.GetLoopRef().GetRaw(), h.GetRaw(), data->sock);
+    if (err < 0) {
+      h.ReportError(err);
+      return;
+    }
+    data->callback();
+  });
+}
+
+void Poll::Start(int events) {
+  Invoke(&uv_poll_start, GetRaw(), events,
+         [](uv_poll_t* handle, int status, int events) {
+           Poll& h = *static_cast<Poll*>(handle->data);
+           if (status < 0) {
+             h.ReportError(status);
+           } else {
+             h.pollEvent(events);
+           }
+         });
+}
+
+}  // namespace wpi::uv
diff --git a/wpinet/src/main/native/cpp/uv/Prepare.cpp b/wpinet/src/main/native/cpp/uv/Prepare.cpp
new file mode 100644
index 0000000..e4ca160
--- /dev/null
+++ b/wpinet/src/main/native/cpp/uv/Prepare.cpp
@@ -0,0 +1,29 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#include "wpinet/uv/Prepare.h"
+
+#include "wpinet/uv/Loop.h"
+
+namespace wpi::uv {
+
+std::shared_ptr<Prepare> Prepare::Create(Loop& loop) {
+  auto h = std::make_shared<Prepare>(private_init{});
+  int err = uv_prepare_init(loop.GetRaw(), h->GetRaw());
+  if (err < 0) {
+    loop.ReportError(err);
+    return nullptr;
+  }
+  h->Keep();
+  return h;
+}
+
+void Prepare::Start() {
+  Invoke(&uv_prepare_start, GetRaw(), [](uv_prepare_t* handle) {
+    Prepare& h = *static_cast<Prepare*>(handle->data);
+    h.prepare();
+  });
+}
+
+}  // namespace wpi::uv
diff --git a/wpinet/src/main/native/cpp/uv/Process.cpp b/wpinet/src/main/native/cpp/uv/Process.cpp
new file mode 100644
index 0000000..3c10db6
--- /dev/null
+++ b/wpinet/src/main/native/cpp/uv/Process.cpp
@@ -0,0 +1,134 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#include "wpinet/uv/Process.h"
+
+#include <wpi/SmallString.h>
+
+#include "wpinet/uv/Loop.h"
+#include "wpinet/uv/Pipe.h"
+
+namespace wpi::uv {
+
+std::shared_ptr<Process> Process::SpawnArray(Loop& loop, std::string_view file,
+                                             std::span<const Option> options) {
+  // convert Option array to libuv structure
+  uv_process_options_t coptions;
+
+  coptions.exit_cb = [](uv_process_t* handle, int64_t status, int signal) {
+    auto& h = *static_cast<Process*>(handle->data);
+    h.exited(status, signal);
+  };
+
+  SmallString<128> fileBuf{file};
+  coptions.file = fileBuf.c_str();
+  coptions.cwd = nullptr;
+  coptions.flags = 0;
+  coptions.uid = 0;
+  coptions.gid = 0;
+
+  SmallVector<char*, 4> argsBuf;
+  SmallVector<char*, 4> envBuf;
+  struct StdioContainer : public uv_stdio_container_t {
+    StdioContainer() {
+      flags = UV_IGNORE;
+      data.fd = 0;
+    }
+  };
+  SmallVector<StdioContainer, 4> stdioBuf;
+
+  for (auto&& o : options) {
+    switch (o.m_type) {
+      case Option::kArg:
+        argsBuf.push_back(const_cast<char*>(o.m_data.str));
+        break;
+      case Option::kEnv:
+        envBuf.push_back(const_cast<char*>(o.m_data.str));
+        break;
+      case Option::kCwd:
+        coptions.cwd = o.m_data.str[0] == '\0' ? nullptr : o.m_data.str;
+        break;
+      case Option::kUid:
+        coptions.uid = o.m_data.uid;
+        coptions.flags |= UV_PROCESS_SETUID;
+        break;
+      case Option::kGid:
+        coptions.gid = o.m_data.gid;
+        coptions.flags |= UV_PROCESS_SETGID;
+        break;
+      case Option::kSetFlags:
+        coptions.flags |= o.m_data.flags;
+        break;
+      case Option::kClearFlags:
+        coptions.flags &= ~o.m_data.flags;
+        break;
+      case Option::kStdioIgnore: {
+        size_t index = o.m_data.stdio.index;
+        if (index >= stdioBuf.size()) {
+          stdioBuf.resize(index + 1);
+        }
+        stdioBuf[index].flags = UV_IGNORE;
+        stdioBuf[index].data.fd = 0;
+        break;
+      }
+      case Option::kStdioInheritFd: {
+        size_t index = o.m_data.stdio.index;
+        if (index >= stdioBuf.size()) {
+          stdioBuf.resize(index + 1);
+        }
+        stdioBuf[index].flags = UV_INHERIT_FD;
+        stdioBuf[index].data.fd = o.m_data.stdio.fd;
+        break;
+      }
+      case Option::kStdioInheritPipe: {
+        size_t index = o.m_data.stdio.index;
+        if (index >= stdioBuf.size()) {
+          stdioBuf.resize(index + 1);
+        }
+        stdioBuf[index].flags = UV_INHERIT_STREAM;
+        stdioBuf[index].data.stream = o.m_data.stdio.pipe->GetRawStream();
+        break;
+      }
+      case Option::kStdioCreatePipe: {
+        size_t index = o.m_data.stdio.index;
+        if (index >= stdioBuf.size()) {
+          stdioBuf.resize(index + 1);
+        }
+        stdioBuf[index].flags =
+            static_cast<uv_stdio_flags>(UV_CREATE_PIPE | o.m_data.stdio.flags);
+        stdioBuf[index].data.stream = o.m_data.stdio.pipe->GetRawStream();
+        break;
+      }
+      default:
+        break;
+    }
+  }
+
+  if (argsBuf.empty()) {
+    argsBuf.push_back(const_cast<char*>(coptions.file));
+  }
+  argsBuf.push_back(nullptr);
+  coptions.args = argsBuf.data();
+
+  if (envBuf.empty()) {
+    coptions.env = nullptr;
+  } else {
+    envBuf.push_back(nullptr);
+    coptions.env = envBuf.data();
+  }
+
+  coptions.stdio_count = stdioBuf.size();
+  coptions.stdio = static_cast<uv_stdio_container_t*>(stdioBuf.data());
+
+  auto h = std::make_shared<Process>(private_init{});
+  int err = uv_spawn(loop.GetRaw(), h->GetRaw(), &coptions);
+  if (err < 0) {
+    loop.ReportError(err);
+    return nullptr;
+  }
+  h->Keep();
+  return h;
+}
+
+}  // namespace wpi::uv
diff --git a/wpinet/src/main/native/cpp/uv/Signal.cpp b/wpinet/src/main/native/cpp/uv/Signal.cpp
new file mode 100644
index 0000000..10dd7b4
--- /dev/null
+++ b/wpinet/src/main/native/cpp/uv/Signal.cpp
@@ -0,0 +1,32 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#include "wpinet/uv/Signal.h"
+
+#include "wpinet/uv/Loop.h"
+
+namespace wpi::uv {
+
+std::shared_ptr<Signal> Signal::Create(Loop& loop) {
+  auto h = std::make_shared<Signal>(private_init{});
+  int err = uv_signal_init(loop.GetRaw(), h->GetRaw());
+  if (err < 0) {
+    loop.ReportError(err);
+    return nullptr;
+  }
+  h->Keep();
+  return h;
+}
+
+void Signal::Start(int signum) {
+  Invoke(
+      &uv_signal_start, GetRaw(),
+      [](uv_signal_t* handle, int signum) {
+        Signal& h = *static_cast<Signal*>(handle->data);
+        h.signal(signum);
+      },
+      signum);
+}
+
+}  // namespace wpi::uv
diff --git a/wpinet/src/main/native/cpp/uv/Stream.cpp b/wpinet/src/main/native/cpp/uv/Stream.cpp
new file mode 100644
index 0000000..e7f6031
--- /dev/null
+++ b/wpinet/src/main/native/cpp/uv/Stream.cpp
@@ -0,0 +1,119 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#include "wpinet/uv/Stream.h"
+
+#include <wpi/SmallVector.h>
+
+using namespace wpi;
+using namespace wpi::uv;
+
+namespace {
+class CallbackWriteReq : public WriteReq {
+ public:
+  CallbackWriteReq(std::span<const Buffer> bufs,
+                   std::function<void(std::span<Buffer>, Error)> callback)
+      : m_bufs{bufs.begin(), bufs.end()} {
+    finish.connect(
+        [this, f = std::move(callback)](Error err) { f(m_bufs, err); });
+  }
+
+ private:
+  SmallVector<Buffer, 4> m_bufs;
+};
+}  // namespace
+
+namespace wpi::uv {
+
+ShutdownReq::ShutdownReq() {
+  error = [this](Error err) { GetStream().error(err); };
+}
+
+WriteReq::WriteReq() {
+  error = [this](Error err) { GetStream().error(err); };
+}
+
+void Stream::Shutdown(const std::shared_ptr<ShutdownReq>& req) {
+  if (Invoke(&uv_shutdown, req->GetRaw(), GetRawStream(),
+             [](uv_shutdown_t* req, int status) {
+               auto& h = *static_cast<ShutdownReq*>(req->data);
+               if (status < 0) {
+                 h.ReportError(status);
+               } else {
+                 h.complete();
+               }
+               h.Release();  // this is always a one-shot
+             })) {
+    req->Keep();
+  }
+}
+
+void Stream::Shutdown(std::function<void()> callback) {
+  auto req = std::make_shared<ShutdownReq>();
+  if (callback) {
+    req->complete.connect(std::move(callback));
+  }
+  Shutdown(req);
+}
+
+void Stream::StartRead() {
+  Invoke(&uv_read_start, GetRawStream(), &Handle::AllocBuf,
+         [](uv_stream_t* stream, ssize_t nread, const uv_buf_t* buf) {
+           auto& h = *static_cast<Stream*>(stream->data);
+           Buffer data = *buf;
+
+           // nread=0 is simply ignored
+           if (nread == UV_EOF) {
+             h.end();
+           } else if (nread > 0) {
+             h.data(data, static_cast<size_t>(nread));
+           } else if (nread < 0) {
+             h.ReportError(nread);
+           }
+
+           // free the buffer
+           h.FreeBuf(data);
+         });
+}
+
+void Stream::Write(std::span<const Buffer> bufs,
+                   const std::shared_ptr<WriteReq>& req) {
+  if (Invoke(&uv_write, req->GetRaw(), GetRawStream(), bufs.data(), bufs.size(),
+             [](uv_write_t* r, int status) {
+               auto& h = *static_cast<WriteReq*>(r->data);
+               if (status < 0) {
+                 h.ReportError(status);
+               }
+               h.finish(Error(status));
+               h.Release();  // this is always a one-shot
+             })) {
+    req->Keep();
+  }
+}
+
+void Stream::Write(std::span<const Buffer> bufs,
+                   std::function<void(std::span<Buffer>, Error)> callback) {
+  Write(bufs, std::make_shared<CallbackWriteReq>(bufs, std::move(callback)));
+}
+
+int Stream::TryWrite(std::span<const Buffer> bufs) {
+  int val = uv_try_write(GetRawStream(), bufs.data(), bufs.size());
+  if (val < 0) {
+    this->ReportError(val);
+    return 0;
+  }
+  return val;
+}
+
+int Stream::TryWrite2(std::span<const Buffer> bufs, Stream& send) {
+  int val = uv_try_write2(GetRawStream(), bufs.data(), bufs.size(),
+                          send.GetRawStream());
+  if (val < 0) {
+    this->ReportError(val);
+    return 0;
+  }
+  return val;
+}
+
+}  // namespace wpi::uv
diff --git a/wpinet/src/main/native/cpp/uv/Tcp.cpp b/wpinet/src/main/native/cpp/uv/Tcp.cpp
new file mode 100644
index 0000000..ae01683
--- /dev/null
+++ b/wpinet/src/main/native/cpp/uv/Tcp.cpp
@@ -0,0 +1,181 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#include "wpinet/uv/Tcp.h"
+
+#include <cstring>
+
+#include "wpinet/uv/util.h"
+
+namespace wpi::uv {
+
+std::shared_ptr<Tcp> Tcp::Create(Loop& loop, unsigned int flags) {
+  auto h = std::make_shared<Tcp>(private_init{});
+  int err = uv_tcp_init_ex(loop.GetRaw(), h->GetRaw(), flags);
+  if (err < 0) {
+    loop.ReportError(err);
+    return nullptr;
+  }
+  h->Keep();
+  return h;
+}
+
+void Tcp::Reuse(std::function<void()> callback, unsigned int flags) {
+  if (IsClosing()) {
+    return;
+  }
+  if (!m_reuseData) {
+    m_reuseData = std::make_unique<ReuseData>();
+  }
+  m_reuseData->callback = std::move(callback);
+  m_reuseData->flags = flags;
+  uv_close(GetRawHandle(), [](uv_handle_t* handle) {
+    Tcp& h = *static_cast<Tcp*>(handle->data);
+    if (!h.m_reuseData) {
+      return;  // just in case
+    }
+    auto data = std::move(h.m_reuseData);
+    int err = uv_tcp_init_ex(h.GetLoopRef().GetRaw(), h.GetRaw(), data->flags);
+    if (err < 0) {
+      h.ReportError(err);
+      return;
+    }
+    data->callback();
+  });
+}
+
+std::shared_ptr<Tcp> Tcp::Accept() {
+  auto client = Create(GetLoopRef());
+  if (!client) {
+    return nullptr;
+  }
+  if (!Accept(client)) {
+    client->Release();
+    return nullptr;
+  }
+  return client;
+}
+
+Tcp* Tcp::DoAccept() {
+  return Accept().get();
+}
+
+void Tcp::Bind(std::string_view ip, unsigned int port, unsigned int flags) {
+  sockaddr_in addr;
+  int err = NameToAddr(ip, port, &addr);
+  if (err < 0) {
+    ReportError(err);
+  } else {
+    Bind(reinterpret_cast<const sockaddr&>(addr), flags);
+  }
+}
+
+void Tcp::Bind6(std::string_view ip, unsigned int port, unsigned int flags) {
+  sockaddr_in6 addr;
+  int err = NameToAddr(ip, port, &addr);
+  if (err < 0) {
+    ReportError(err);
+  } else {
+    Bind(reinterpret_cast<const sockaddr&>(addr), flags);
+  }
+}
+
+sockaddr_storage Tcp::GetSock() {
+  sockaddr_storage name;
+  int len = sizeof(name);
+  if (!Invoke(&uv_tcp_getsockname, GetRaw(), reinterpret_cast<sockaddr*>(&name),
+              &len)) {
+    std::memset(&name, 0, sizeof(name));
+  }
+  return name;
+}
+
+sockaddr_storage Tcp::GetPeer() {
+  sockaddr_storage name;
+  int len = sizeof(name);
+  if (!Invoke(&uv_tcp_getpeername, GetRaw(), reinterpret_cast<sockaddr*>(&name),
+              &len)) {
+    std::memset(&name, 0, sizeof(name));
+  }
+  return name;
+}
+
+void Tcp::Connect(const sockaddr& addr,
+                  const std::shared_ptr<TcpConnectReq>& req) {
+  if (Invoke(&uv_tcp_connect, req->GetRaw(), GetRaw(), &addr,
+             [](uv_connect_t* req, int status) {
+               auto& h = *static_cast<TcpConnectReq*>(req->data);
+               if (status < 0) {
+                 h.ReportError(status);
+               } else {
+                 h.connected();
+               }
+               h.Release();  // this is always a one-shot
+             })) {
+    req->Keep();
+  }
+}
+
+void Tcp::Connect(const sockaddr& addr, std::function<void()> callback) {
+  auto req = std::make_shared<TcpConnectReq>();
+  req->connected.connect(std::move(callback));
+  Connect(addr, req);
+}
+
+void Tcp::Connect(std::string_view ip, unsigned int port,
+                  const std::shared_ptr<TcpConnectReq>& req) {
+  sockaddr_in addr;
+  int err = NameToAddr(ip, port, &addr);
+  if (err < 0) {
+    ReportError(err);
+  } else {
+    Connect(reinterpret_cast<const sockaddr&>(addr), req);
+  }
+}
+
+void Tcp::Connect(std::string_view ip, unsigned int port,
+                  std::function<void()> callback) {
+  sockaddr_in addr;
+  int err = NameToAddr(ip, port, &addr);
+  if (err < 0) {
+    ReportError(err);
+  } else {
+    Connect(reinterpret_cast<const sockaddr&>(addr), std::move(callback));
+  }
+}
+
+void Tcp::Connect6(std::string_view ip, unsigned int port,
+                   const std::shared_ptr<TcpConnectReq>& req) {
+  sockaddr_in6 addr;
+  int err = NameToAddr(ip, port, &addr);
+  if (err < 0) {
+    ReportError(err);
+  } else {
+    Connect(reinterpret_cast<const sockaddr&>(addr), req);
+  }
+}
+
+void Tcp::Connect6(std::string_view ip, unsigned int port,
+                   std::function<void()> callback) {
+  sockaddr_in6 addr;
+  int err = NameToAddr(ip, port, &addr);
+  if (err < 0) {
+    ReportError(err);
+  } else {
+    Connect(reinterpret_cast<const sockaddr&>(addr), std::move(callback));
+  }
+}
+
+void Tcp::CloseReset() {
+  if (!IsClosing()) {
+    uv_tcp_close_reset(GetRaw(), [](uv_handle_t* handle) {
+      Tcp& h = *static_cast<Tcp*>(handle->data);
+      h.closed();
+      h.Release();  // free ourselves
+    });
+    ForceClosed();
+  }
+}
+
+}  // namespace wpi::uv
diff --git a/wpinet/src/main/native/cpp/uv/Timer.cpp b/wpinet/src/main/native/cpp/uv/Timer.cpp
new file mode 100644
index 0000000..9d52173
--- /dev/null
+++ b/wpinet/src/main/native/cpp/uv/Timer.cpp
@@ -0,0 +1,44 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#include "wpinet/uv/Timer.h"
+
+#include "wpinet/uv/Loop.h"
+
+namespace wpi::uv {
+
+std::shared_ptr<Timer> Timer::Create(Loop& loop) {
+  auto h = std::make_shared<Timer>(private_init{});
+  int err = uv_timer_init(loop.GetRaw(), h->GetRaw());
+  if (err < 0) {
+    loop.ReportError(err);
+    return nullptr;
+  }
+  h->Keep();
+  return h;
+}
+
+void Timer::SingleShot(Loop& loop, Time timeout, std::function<void()> func) {
+  auto h = Create(loop);
+  if (!h) {
+    return;
+  }
+  h->timeout.connect([theTimer = h.get(), f = std::move(func)]() {
+    f();
+    theTimer->Close();
+  });
+  h->Start(timeout);
+}
+
+void Timer::Start(Time timeout, Time repeat) {
+  Invoke(
+      &uv_timer_start, GetRaw(),
+      [](uv_timer_t* handle) {
+        Timer& h = *static_cast<Timer*>(handle->data);
+        h.timeout();
+      },
+      timeout.count(), repeat.count());
+}
+
+}  // namespace wpi::uv
diff --git a/wpinet/src/main/native/cpp/uv/Tty.cpp b/wpinet/src/main/native/cpp/uv/Tty.cpp
new file mode 100644
index 0000000..6043a93
--- /dev/null
+++ b/wpinet/src/main/native/cpp/uv/Tty.cpp
@@ -0,0 +1,22 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#include "wpinet/uv/Tty.h"
+
+#include "wpinet/uv/Loop.h"
+
+namespace wpi::uv {
+
+std::shared_ptr<Tty> Tty::Create(Loop& loop, uv_file fd, bool readable) {
+  auto h = std::make_shared<Tty>(private_init{});
+  int err = uv_tty_init(loop.GetRaw(), h->GetRaw(), fd, readable ? 1 : 0);
+  if (err < 0) {
+    loop.ReportError(err);
+    return nullptr;
+  }
+  h->Keep();
+  return h;
+}
+
+}  // namespace wpi::uv
diff --git a/wpinet/src/main/native/cpp/uv/Udp.cpp b/wpinet/src/main/native/cpp/uv/Udp.cpp
new file mode 100644
index 0000000..689d5a7
--- /dev/null
+++ b/wpinet/src/main/native/cpp/uv/Udp.cpp
@@ -0,0 +1,196 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#include "wpinet/uv/Udp.h"
+
+#include <cstring>
+
+#include <wpi/SmallString.h>
+#include <wpi/SmallVector.h>
+
+#include "wpinet/uv/util.h"
+
+namespace {
+
+using namespace wpi;
+using namespace wpi::uv;
+
+class CallbackUdpSendReq : public UdpSendReq {
+ public:
+  CallbackUdpSendReq(std::span<const Buffer> bufs,
+                     std::function<void(std::span<Buffer>, Error)> callback)
+      : m_bufs{bufs.begin(), bufs.end()} {
+    complete.connect(
+        [this, f = std::move(callback)](Error err) { f(m_bufs, err); });
+  }
+
+ private:
+  SmallVector<Buffer, 4> m_bufs;
+};
+
+}  // namespace
+
+namespace wpi::uv {
+
+UdpSendReq::UdpSendReq() {
+  error = [this](Error err) { GetUdp().error(err); };
+}
+
+std::shared_ptr<Udp> Udp::Create(Loop& loop, unsigned int flags) {
+  auto h = std::make_shared<Udp>(private_init{});
+  int err = uv_udp_init_ex(loop.GetRaw(), h->GetRaw(), flags);
+  if (err < 0) {
+    loop.ReportError(err);
+    return nullptr;
+  }
+  h->Keep();
+  return h;
+}
+
+void Udp::Bind(std::string_view ip, unsigned int port, unsigned int flags) {
+  sockaddr_in addr;
+  int err = NameToAddr(ip, port, &addr);
+  if (err < 0) {
+    ReportError(err);
+  } else {
+    Bind(reinterpret_cast<const sockaddr&>(addr), flags);
+  }
+}
+
+void Udp::Bind6(std::string_view ip, unsigned int port, unsigned int flags) {
+  sockaddr_in6 addr;
+  int err = NameToAddr(ip, port, &addr);
+  if (err < 0) {
+    ReportError(err);
+  } else {
+    Bind(reinterpret_cast<const sockaddr&>(addr), flags);
+  }
+}
+
+void Udp::Connect(std::string_view ip, unsigned int port) {
+  sockaddr_in addr;
+  int err = NameToAddr(ip, port, &addr);
+  if (err < 0) {
+    ReportError(err);
+  } else {
+    Connect(reinterpret_cast<const sockaddr&>(addr));
+  }
+}
+
+void Udp::Connect6(std::string_view ip, unsigned int port) {
+  sockaddr_in6 addr;
+  int err = NameToAddr(ip, port, &addr);
+  if (err < 0) {
+    ReportError(err);
+  } else {
+    Connect(reinterpret_cast<const sockaddr&>(addr));
+  }
+}
+
+sockaddr_storage Udp::GetPeer() {
+  sockaddr_storage name;
+  int len = sizeof(name);
+  if (!Invoke(&uv_udp_getpeername, GetRaw(), reinterpret_cast<sockaddr*>(&name),
+              &len)) {
+    std::memset(&name, 0, sizeof(name));
+  }
+  return name;
+}
+
+sockaddr_storage Udp::GetSock() {
+  sockaddr_storage name;
+  int len = sizeof(name);
+  if (!Invoke(&uv_udp_getsockname, GetRaw(), reinterpret_cast<sockaddr*>(&name),
+              &len)) {
+    std::memset(&name, 0, sizeof(name));
+  }
+  return name;
+}
+
+void Udp::SetMembership(std::string_view multicastAddr,
+                        std::string_view interfaceAddr,
+                        uv_membership membership) {
+  SmallString<128> multicastAddrBuf{multicastAddr};
+  SmallString<128> interfaceAddrBuf{interfaceAddr};
+  Invoke(&uv_udp_set_membership, GetRaw(), multicastAddrBuf.c_str(),
+         interfaceAddrBuf.c_str(), membership);
+}
+
+void Udp::SetSourceMembership(std::string_view multicastAddr,
+                              std::string_view interfaceAddr,
+                              std::string_view sourceAddr,
+                              uv_membership membership) {
+  SmallString<128> multicastAddrBuf{multicastAddr};
+  SmallString<128> interfaceAddrBuf{interfaceAddr};
+  SmallString<128> sourceAddrBuf{sourceAddr};
+  Invoke(&uv_udp_set_source_membership, GetRaw(), multicastAddrBuf.c_str(),
+         interfaceAddrBuf.c_str(), sourceAddrBuf.c_str(), membership);
+}
+
+void Udp::SetMulticastInterface(std::string_view interfaceAddr) {
+  SmallString<128> interfaceAddrBuf{interfaceAddr};
+  Invoke(&uv_udp_set_multicast_interface, GetRaw(), interfaceAddrBuf.c_str());
+}
+
+void Udp::Send(const sockaddr& addr, std::span<const Buffer> bufs,
+               const std::shared_ptr<UdpSendReq>& req) {
+  if (Invoke(&uv_udp_send, req->GetRaw(), GetRaw(), bufs.data(), bufs.size(),
+             &addr, [](uv_udp_send_t* r, int status) {
+               auto& h = *static_cast<UdpSendReq*>(r->data);
+               if (status < 0) {
+                 h.ReportError(status);
+               }
+               h.complete(Error(status));
+               h.Release();  // this is always a one-shot
+             })) {
+    req->Keep();
+  }
+}
+
+void Udp::Send(const sockaddr& addr, std::span<const Buffer> bufs,
+               std::function<void(std::span<Buffer>, Error)> callback) {
+  Send(addr, bufs,
+       std::make_shared<CallbackUdpSendReq>(bufs, std::move(callback)));
+}
+
+void Udp::Send(std::span<const Buffer> bufs,
+               const std::shared_ptr<UdpSendReq>& req) {
+  if (Invoke(&uv_udp_send, req->GetRaw(), GetRaw(), bufs.data(), bufs.size(),
+             nullptr, [](uv_udp_send_t* r, int status) {
+               auto& h = *static_cast<UdpSendReq*>(r->data);
+               if (status < 0) {
+                 h.ReportError(status);
+               }
+               h.complete(Error(status));
+               h.Release();  // this is always a one-shot
+             })) {
+    req->Keep();
+  }
+}
+
+void Udp::Send(std::span<const Buffer> bufs,
+               std::function<void(std::span<Buffer>, Error)> callback) {
+  Send(bufs, std::make_shared<CallbackUdpSendReq>(bufs, std::move(callback)));
+}
+
+void Udp::StartRecv() {
+  Invoke(&uv_udp_recv_start, GetRaw(), &AllocBuf,
+         [](uv_udp_t* handle, ssize_t nread, const uv_buf_t* buf,
+            const sockaddr* addr, unsigned flags) {
+           auto& h = *static_cast<Udp*>(handle->data);
+           Buffer data = *buf;
+
+           // nread=0 is simply ignored
+           if (nread > 0) {
+             h.received(data, static_cast<size_t>(nread), *addr, flags);
+           } else if (nread < 0) {
+             h.ReportError(nread);
+           }
+
+           // free the buffer
+           h.FreeBuf(data);
+         });
+}
+
+}  // namespace wpi::uv
diff --git a/wpinet/src/main/native/cpp/uv/Work.cpp b/wpinet/src/main/native/cpp/uv/Work.cpp
new file mode 100644
index 0000000..818a93b
--- /dev/null
+++ b/wpinet/src/main/native/cpp/uv/Work.cpp
@@ -0,0 +1,50 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#include "wpinet/uv/Work.h"
+
+#include "wpinet/uv/Loop.h"
+
+namespace wpi::uv {
+
+WorkReq::WorkReq() {
+  error = [this](Error err) { GetLoop().error(err); };
+}
+
+void QueueWork(Loop& loop, const std::shared_ptr<WorkReq>& req) {
+  int err = uv_queue_work(
+      loop.GetRaw(), req->GetRaw(),
+      [](uv_work_t* req) {
+        auto& h = *static_cast<WorkReq*>(req->data);
+        h.work();
+      },
+      [](uv_work_t* req, int status) {
+        auto& h = *static_cast<WorkReq*>(req->data);
+        if (status < 0) {
+          h.ReportError(status);
+        } else {
+          h.afterWork();
+        }
+        h.Release();  // this is always a one-shot
+      });
+  if (err < 0) {
+    loop.ReportError(err);
+  } else {
+    req->Keep();
+  }
+}
+
+void QueueWork(Loop& loop, std::function<void()> work,
+               std::function<void()> afterWork) {
+  auto req = std::make_shared<WorkReq>();
+  if (work) {
+    req->work.connect(std::move(work));
+  }
+  if (afterWork) {
+    req->afterWork.connect(std::move(afterWork));
+  }
+  QueueWork(loop, req);
+}
+
+}  // namespace wpi::uv
diff --git a/wpinet/src/main/native/include/wpinet/DsClient.h b/wpinet/src/main/native/include/wpinet/DsClient.h
new file mode 100644
index 0000000..dcebe2f
--- /dev/null
+++ b/wpinet/src/main/native/include/wpinet/DsClient.h
@@ -0,0 +1,55 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#pragma once
+
+#include <memory>
+#include <string>
+#include <string_view>
+
+#include <wpi/Signal.h>
+
+namespace wpi {
+
+class Logger;
+
+namespace uv {
+class Loop;
+class Tcp;
+class Timer;
+}  // namespace uv
+
+class DsClient : public std::enable_shared_from_this<DsClient> {
+  struct private_init {};
+
+ public:
+  static std::shared_ptr<DsClient> Create(wpi::uv::Loop& loop,
+                                          wpi::Logger& logger) {
+    return std::make_shared<DsClient>(loop, logger, private_init{});
+  }
+
+  DsClient(wpi::uv::Loop& loop, wpi::Logger& logger, const private_init&);
+  ~DsClient();
+  DsClient(const DsClient&) = delete;
+  DsClient& operator=(const DsClient&) = delete;
+
+  void Close();
+
+  sig::Signal<std::string_view> setIp;
+  sig::Signal<> clearIp;
+
+ private:
+  void Connect();
+  void HandleIncoming(std::string_view in);
+  void ParseJson();
+
+  wpi::Logger& m_logger;
+
+  std::shared_ptr<wpi::uv::Tcp> m_tcp;
+  std::shared_ptr<wpi::uv::Timer> m_timer;
+
+  std::string m_json;
+};
+
+}  // namespace wpi
diff --git a/wpinet/src/main/native/include/wpinet/EventLoopRunner.h b/wpinet/src/main/native/include/wpinet/EventLoopRunner.h
new file mode 100644
index 0000000..896bae3
--- /dev/null
+++ b/wpinet/src/main/native/include/wpinet/EventLoopRunner.h
@@ -0,0 +1,63 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#ifndef WPINET_EVENTLOOPRUNNER_H_
+#define WPINET_EVENTLOOPRUNNER_H_
+
+#include <functional>
+#include <memory>
+
+#include <wpi/SafeThread.h>
+
+#include "wpinet/uv/Loop.h"
+
+namespace wpi {
+
+/**
+ * Executes an event loop on a separate thread.
+ */
+class EventLoopRunner {
+ public:
+  using LoopFunc = std::function<void(uv::Loop&)>;
+
+  EventLoopRunner();
+  virtual ~EventLoopRunner();
+
+  /**
+   * Stop the loop.  Once the loop is stopped it cannot be restarted.
+   * This function does not return until the loop has exited.
+   */
+  void Stop();
+
+  /**
+   * Run a function asynchronously (once) on the loop.
+   * This is safe to call from any thread, but is NOT safe to call from the
+   * provided function (it will deadlock).
+   * @param func function to execute on the loop
+   */
+  void ExecAsync(LoopFunc func);
+
+  /**
+   * Run a function synchronously (once) on the loop.
+   * This is safe to call from any thread, but is NOT safe to call from the
+   * provided function (it will deadlock).
+   * This does not return until the function finishes executing.
+   * @param func function to execute on the loop
+   */
+  void ExecSync(LoopFunc func);
+
+  /**
+   * Get the loop.  If the loop thread is not running, returns nullptr.
+   * @return The loop
+   */
+  std::shared_ptr<uv::Loop> GetLoop();
+
+ private:
+  class Thread;
+  SafeThreadOwner<Thread> m_owner;
+};
+
+}  // namespace wpi
+
+#endif  // WPINET_EVENTLOOPRUNNER_H_
diff --git a/wpinet/src/main/native/include/wpinet/HttpParser.h b/wpinet/src/main/native/include/wpinet/HttpParser.h
new file mode 100644
index 0000000..a2a6ea7
--- /dev/null
+++ b/wpinet/src/main/native/include/wpinet/HttpParser.h
@@ -0,0 +1,228 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#ifndef WPINET_HTTPPARSER_H_
+#define WPINET_HTTPPARSER_H_
+
+#include <stdint.h>
+
+#include <string_view>
+
+#include <wpi/Signal.h>
+#include <wpi/SmallString.h>
+
+#include "wpinet/http_parser.h"
+
+namespace wpi {
+
+/**
+ * HTTP protocol parser.  Performs incremental parsing with callbacks for each
+ * part of the HTTP protocol.  As this is incremental, it's suitable for use
+ * with event based frameworks that provide arbitrary chunks of data.
+ */
+class HttpParser {
+ public:
+  enum Type {
+    kRequest = HTTP_REQUEST,
+    kResponse = HTTP_RESPONSE,
+    kBoth = HTTP_BOTH
+  };
+
+  /**
+   * Returns the library version. Bits 16-23 contain the major version number,
+   * bits 8-15 the minor version number and bits 0-7 the patch level.
+   */
+  static uint32_t GetParserVersion();
+
+  /**
+   * Constructor.
+   * @param type Type of parser (request or response or both)
+   */
+  explicit HttpParser(Type type);
+
+  /**
+   * Reset the parser to initial state.
+   * This allows reusing the same parser object from request to request.
+   * @param type Type of parser (request or response or both)
+   */
+  void Reset(Type type);
+
+  /**
+   * Set the maximum accepted length for URLs, field names, and field values.
+   * The default is 1024.
+   * @param len maximum length
+   */
+  void SetMaxLength(size_t len) { m_maxLength = len; }
+
+  /**
+   * Executes the parser.  An empty input is treated as EOF.
+   * @param in input data
+   * @return Trailing input data after the parse.
+   */
+  std::string_view Execute(std::string_view in) {
+    in.remove_prefix(
+        http_parser_execute(&m_parser, &m_settings, in.data(), in.size()));
+    return in;
+  }
+
+  /**
+   * Get HTTP major version.
+   */
+  unsigned int GetMajor() const { return m_parser.http_major; }
+
+  /**
+   * Get HTTP minor version.
+   */
+  unsigned int GetMinor() const { return m_parser.http_minor; }
+
+  /**
+   * Get HTTP status code.  Valid only on responses.  Valid in and after
+   * the OnStatus() callback has been called.
+   */
+  unsigned int GetStatusCode() const { return m_parser.status_code; }
+
+  /**
+   * Get HTTP method.  Valid only on requests.
+   */
+  http_method GetMethod() const {
+    return static_cast<http_method>(m_parser.method);
+  }
+
+  /**
+   * Determine if an error occurred.
+   * @return False if no error.
+   */
+  bool HasError() const { return m_parser.http_errno != HPE_OK; }
+
+  /**
+   * Get error number.
+   */
+  http_errno GetError() const {
+    return static_cast<http_errno>(m_parser.http_errno);
+  }
+
+  /**
+   * Abort the parse.  Call this from a callback handler to indicate an error.
+   * This will result in GetError() returning one of the callback-related
+   * errors (e.g. HPE_CB_message_begin).
+   */
+  void Abort() { m_aborted = true; }
+
+  /**
+   * Determine if an upgrade header was present and the parser has exited
+   * because of that.  Should be checked when Execute() returns in addition to
+   * checking GetError().
+   * @return True if upgrade header, false otherwise.
+   */
+  bool IsUpgrade() const { return m_parser.upgrade; }
+
+  /**
+   * If this returns false in the headersComplete or messageComplete
+   * callback, then this should be the last message on the connection.
+   * If you are the server, respond with the "Connection: close" header.
+   * If you are the client, close the connection.
+   */
+  bool ShouldKeepAlive() const { return http_should_keep_alive(&m_parser); }
+
+  /**
+   * Pause the parser.
+   * @param paused True to pause, false to unpause.
+   */
+  void Pause(bool paused) { http_parser_pause(&m_parser, paused); }
+
+  /**
+   * Checks if this is the final chunk of the body.
+   */
+  bool IsBodyFinal() const { return http_body_is_final(&m_parser); }
+
+  /**
+   * Get URL.  Valid in and after the url callback has been called.
+   */
+  std::string_view GetUrl() const { return m_urlBuf.str(); }
+
+  /**
+   * Message begin callback.
+   */
+  sig::Signal<> messageBegin;
+
+  /**
+   * URL callback.
+   *
+   * The parameter to the callback is the complete URL string.
+   */
+  sig::Signal<std::string_view> url;
+
+  /**
+   * Status callback.
+   *
+   * The parameter to the callback is the complete status string.
+   * GetStatusCode() can be used to get the numeric status code.
+   */
+  sig::Signal<std::string_view> status;
+
+  /**
+   * Header field callback.
+   *
+   * The parameters to the callback are the field name and field value.
+   */
+  sig::Signal<std::string_view, std::string_view> header;
+
+  /**
+   * Headers complete callback.
+   *
+   * The parameter to the callback is whether the connection should be kept
+   * alive.  If this is false, then this should be the last message on the
+   * connection.  If you are the server, respond with the "Connection: close"
+   * header.  If you are the client, close the connection.
+   */
+  sig::Signal<bool> headersComplete;
+
+  /**
+   * Body data callback.
+   *
+   * The parameters to the callback is the data chunk and whether this is the
+   * final chunk of data in the message.  Note this callback will be called
+   * multiple times arbitrarily (e.g. it's possible that it may be called with
+   * just a few characters at a time).
+   */
+  sig::Signal<std::string_view, bool> body;
+
+  /**
+   * Headers complete callback.
+   *
+   * The parameter to the callback is whether the connection should be kept
+   * alive.  If this is false, then this should be the last message on the
+   * connection.  If you are the server, respond with the "Connection: close"
+   * header.  If you are the client, close the connection.
+   */
+  sig::Signal<bool> messageComplete;
+
+  /**
+   * Chunk header callback.
+   *
+   * The parameter to the callback is the chunk size.
+   */
+  sig::Signal<uint64_t> chunkHeader;
+
+  /**
+   * Chunk complete callback.
+   */
+  sig::Signal<> chunkComplete;
+
+ private:
+  http_parser m_parser;
+  http_parser_settings m_settings;
+
+  size_t m_maxLength = 1024;
+  enum { kStart, kUrl, kStatus, kField, kValue } m_state = kStart;
+  SmallString<128> m_urlBuf;
+  SmallString<32> m_fieldBuf;
+  SmallString<128> m_valueBuf;
+
+  bool m_aborted = false;
+};
+
+}  // namespace wpi
+
+#endif  // WPINET_HTTPPARSER_H_
diff --git a/wpinet/src/main/native/include/wpinet/HttpServerConnection.h b/wpinet/src/main/native/include/wpinet/HttpServerConnection.h
new file mode 100644
index 0000000..486f36e
--- /dev/null
+++ b/wpinet/src/main/native/include/wpinet/HttpServerConnection.h
@@ -0,0 +1,152 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#ifndef WPINET_HTTPSERVERCONNECTION_H_
+#define WPINET_HTTPSERVERCONNECTION_H_
+
+#include <memory>
+#include <span>
+#include <string_view>
+
+#include "wpinet/HttpParser.h"
+#include "wpinet/uv/Stream.h"
+
+namespace wpi {
+
+class raw_ostream;
+
+class HttpServerConnection {
+ public:
+  explicit HttpServerConnection(std::shared_ptr<uv::Stream> stream);
+  virtual ~HttpServerConnection() = default;
+
+ protected:
+  /**
+   * Process an incoming HTTP request.  This is called after the incoming
+   * message completes (e.g. from the HttpParser::messageComplete callback).
+   *
+   * The implementation should read request details from m_request and call the
+   * appropriate Send() functions to send a response back to the client.
+   */
+  virtual void ProcessRequest() = 0;
+
+  /**
+   * Build common response headers.
+   *
+   * Called by SendHeader() to send headers common to every response.
+   * Each line must be terminated with "\r\n".
+   *
+   * The default implementation sends the following:
+   * "Server: WebServer/1.0\r\n"
+   * "Cache-Control: no-store, no-cache, must-revalidate, pre-check=0, "
+   * "post-check=0, max-age=0\r\n"
+   * "Pragma: no-cache\r\n"
+   * "Expires: Mon, 3 Jan 2000 12:34:56 GMT\r\n"
+   *
+   * These parameters should ensure the browser does not cache the response.
+   * A browser should connect for each file and not serve files from its cache.
+   *
+   * @param os response stream
+   */
+  virtual void BuildCommonHeaders(raw_ostream& os);
+
+  /**
+   * Build HTTP response header, along with other header information like
+   * mimetype.  Calls BuildCommonHeaders().
+   *
+   * @param os response stream
+   * @param code HTTP response code (e.g. 200)
+   * @param codeText HTTP response code text (e.g. "OK")
+   * @param contentType MIME content type (e.g. "text/plain")
+   * @param contentLength Length of content.  If 0 is provided, m_keepAlive will
+   *                      be set to false.
+   * @param extra Extra HTTP headers to send, including final "\r\n"
+   */
+  virtual void BuildHeader(raw_ostream& os, int code, std::string_view codeText,
+                           std::string_view contentType, uint64_t contentLength,
+                           std::string_view extra = {});
+
+  /**
+   * Send data to client.
+   *
+   * This is a convenience wrapper around m_stream.Write() to provide
+   * auto-close functionality.
+   *
+   * @param bufs Buffers to write.  Deallocate() will be called on each
+   *             buffer after the write completes.  If different behavior
+   *             is desired, call m_stream.Write() directly instead.
+   * @param closeAfter close the connection after the write completes
+   */
+  void SendData(std::span<const uv::Buffer> bufs, bool closeAfter = false);
+
+  /**
+   * Send HTTP response, along with other header information like mimetype.
+   * Calls BuildHeader().
+   *
+   * @param code HTTP response code (e.g. 200)
+   * @param codeText HTTP response code text (e.g. "OK")
+   * @param contentType MIME content type (e.g. "text/plain")
+   * @param content Response message content
+   * @param extraHeader Extra HTTP headers to send, including final "\r\n"
+   */
+  virtual void SendResponse(int code, std::string_view codeText,
+                            std::string_view contentType,
+                            std::string_view content,
+                            std::string_view extraHeader = {});
+
+  /**
+   * Send HTTP response from static data, along with other header information
+   * like mimetype.  Calls BuildHeader().  Supports gzip pre-compressed data
+   * (it will decompress if the client does not accept gzip encoded data).
+   * Unlike SendResponse(), content is not copied and its contents must remain
+   * valid for an unspecified lifetime.
+   *
+   * @param code HTTP response code (e.g. 200)
+   * @param codeText HTTP response code text (e.g. "OK")
+   * @param contentType MIME content type (e.g. "text/plain")
+   * @param content Response message content
+   * @param gzipped True if content is gzip compressed
+   * @param extraHeader Extra HTTP headers to send, including final "\r\n"
+   */
+  virtual void SendStaticResponse(int code, std::string_view codeText,
+                                  std::string_view contentType,
+                                  std::string_view content, bool gzipped,
+                                  std::string_view extraHeader = {});
+
+  /**
+   * Send error header and message.
+   * This provides standard code responses for 400, 401, 403, 404, 500, and 503.
+   * Other codes will be reported as 501.  For arbitrary code handling, use
+   * SendResponse() instead.
+   *
+   * @param code HTTP error code (e.g. 404)
+   * @param message Additional message text
+   */
+  virtual void SendError(int code, std::string_view message = {});
+
+  /** The HTTP request. */
+  HttpParser m_request{HttpParser::kRequest};
+
+  /** Whether the connection should be kept alive. */
+  bool m_keepAlive = false;
+
+  /** If gzip is an acceptable encoding for responses. */
+  bool m_acceptGzip = false;
+
+  /** The underlying stream for the connection. */
+  uv::Stream& m_stream;
+
+  /** The header reader connection. */
+  sig::ScopedConnection m_dataConn;
+
+  /** The end stream connection. */
+  sig::ScopedConnection m_endConn;
+
+  /** The message complete connection. */
+  sig::Connection m_messageCompleteConn;
+};
+
+}  // namespace wpi
+
+#endif  // WPINET_HTTPSERVERCONNECTION_H_
diff --git a/wpinet/src/main/native/include/wpinet/HttpUtil.h b/wpinet/src/main/native/include/wpinet/HttpUtil.h
new file mode 100644
index 0000000..fcf6887
--- /dev/null
+++ b/wpinet/src/main/native/include/wpinet/HttpUtil.h
@@ -0,0 +1,423 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#ifndef WPINET_HTTPUTIL_H_
+#define WPINET_HTTPUTIL_H_
+
+#include <initializer_list>
+#include <memory>
+#include <optional>
+#include <span>
+#include <string>
+#include <string_view>
+#include <utility>
+#include <vector>
+
+#include <wpi/SmallString.h>
+#include <wpi/SmallVector.h>
+#include <wpi/StringMap.h>
+#include <wpi/raw_istream.h>
+
+#include "wpinet/NetworkStream.h"
+#include "wpinet/raw_socket_istream.h"
+#include "wpinet/raw_socket_ostream.h"
+
+namespace wpi {
+
+// Unescape a %xx-encoded URI.
+// @param buf Buffer for output
+// @param error Set to true if an error occurred
+// @return Escaped string
+std::string_view UnescapeURI(std::string_view str, SmallVectorImpl<char>& buf,
+                             bool* error);
+
+// Escape a string with %xx-encoding.
+// @param buf Buffer for output
+// @param spacePlus If true, encodes spaces to '+' rather than "%20"
+// @return Escaped string
+std::string_view EscapeURI(std::string_view str, SmallVectorImpl<char>& buf,
+                           bool spacePlus = true);
+
+// Parse a set of HTTP headers.  Saves just the Content-Type and Content-Length
+// fields.
+// @param is Input stream
+// @param contentType If not null, Content-Type contents are saved here.
+// @param contentLength If not null, Content-Length contents are saved here.
+// @return False if error occurred in input stream
+bool ParseHttpHeaders(raw_istream& is, SmallVectorImpl<char>* contentType,
+                      SmallVectorImpl<char>* contentLength);
+
+// Look for a MIME multi-part boundary.  On return, the input stream will
+// be located at the character following the boundary (usually "\r\n").
+// @param is Input stream
+// @param boundary Boundary string to scan for (not including "--" prefix)
+// @param saveBuf If not null, all scanned characters up to but not including
+//     the boundary are saved to this string
+// @return False if error occurred on input stream, true if boundary found.
+bool FindMultipartBoundary(wpi::raw_istream& is, std::string_view boundary,
+                           std::string* saveBuf);
+
+/**
+ * Map for looking up elements of the query portion of a URI.  Does not
+ * handle multiple elements with the same name.  This is a reference type;
+ * it does not make a copy of the query string, so it is important that the
+ * query string has a lifetime at least as long as this object.
+ */
+class HttpQueryMap {
+ public:
+  /**
+   * Constructs an empty map (with no entries).
+   */
+  HttpQueryMap() = default;
+
+  /**
+   * Constructs from an escaped query string.  Note: does not make a copy of
+   * the query string, so it must not be a temporary.
+   *
+   * @param query query string
+   */
+  explicit HttpQueryMap(std::string_view query);
+
+  /**
+   * Gets an element of the query string.  Both the name and the returned
+   * value are unescaped strings.
+   *
+   * @param name name (unescaped)
+   * @param buf result buffer for value
+   * @return Optional unescaped value.  Returns an empty optional if the
+   *         name is not present in the query map.
+   */
+  std::optional<std::string_view> Get(std::string_view name,
+                                      SmallVectorImpl<char>& buf) const;
+
+ private:
+  StringMap<std::string_view> m_elems;
+};
+
+class HttpPathRef;
+
+/**
+ * Class for HTTP path matching.  A root path is represented as a single
+ * empty element, otherwise the path consists of each non-empty element
+ * between the '/' characters:
+ *  - "" -> []
+ *  - "/" -> [""]
+ *  - "/foo" -> ["foo"]
+ *  - "/foo/bar" -> ["foo", "bar"]
+ *  - "/foo//bar/" -> ["foo", "bar"]
+ *
+ * All path elements are unescaped.
+ */
+class HttpPath {
+ public:
+  /**
+   * Constructs an empty HTTP path.
+   */
+  HttpPath() = default;
+
+  /**
+   * Constructs a HTTP path from an escaped path string.  Makes a copy of the
+   * path, so it's safe to be a temporary.
+   */
+  explicit HttpPath(std::string_view path);
+
+  /**
+   * Evaluates to true if the path is not empty.
+   */
+  explicit operator bool() const { return !empty(); }
+
+  /**
+   * Returns true if the path has no elements.
+   */
+  bool empty() const { return m_pathEnds.empty(); }
+
+  /**
+   * Returns number of elements in the path.
+   */
+  size_t size() const { return m_pathEnds.size(); }
+
+  /**
+   * Returns true if the path exactly matches the provided match list.
+   *
+   * @param match match list
+   * @return True if path equals match list
+   */
+  bool equals(std::initializer_list<std::string_view> match) const {
+    return equals(0, {match.begin(), match.end()});
+  }
+  bool equals(std::span<const std::string_view> match) const {
+    return equals(0, match);
+  }
+  bool equals(std::string_view match) const { return equals(0, {match}); }
+
+  /**
+   * Returns true if the elements of the path starting at the "start" element
+   * match the provided match list, and there are no additional elements.
+   *
+   * @param start element to start matching at
+   * @param match match list
+   * @return True if match
+   */
+  bool equals(size_t start,
+              std::initializer_list<std::string_view> match) const {
+    return equals(start, {match.begin(), match.end()});
+  }
+  bool equals(size_t start, std::span<const std::string_view> match) const {
+    if (m_pathEnds.size() != (start + match.size())) {
+      return false;
+    }
+    return startswith(start, match);
+  }
+  bool equals(size_t start, std::string_view match) const {
+    return equals(start, {match});
+  }
+
+  /**
+   * Returns true if the first elements of the path match the provided match
+   * list.  The path may have additional elements.
+   *
+   * @param match match list
+   * @return True if path starts with match list
+   */
+  bool startswith(std::initializer_list<std::string_view> match) const {
+    return startswith(0, {match.begin(), match.end()});
+  }
+  bool startswith(std::span<const std::string_view> match) const {
+    return startswith(0, match);
+  }
+  bool startswith(std::string_view match) const {
+    return startswith(0, {match});
+  }
+
+  /**
+   * Returns true if the elements of the path starting at the "start" element
+   * match the provided match list.  The path may have additional elements.
+   *
+   * @param start element to start matching at
+   * @param match match list
+   * @return True if path starting at the start element matches the match list
+   */
+  bool startswith(size_t start,
+                  std::initializer_list<std::string_view> match) const {
+    return startswith(start, {match.begin(), match.end()});
+  }
+
+  bool startswith(size_t start, std::span<const std::string_view> match) const;
+
+  bool startswith(size_t start, std::string_view match) const {
+    return startswith(start, {match});
+  }
+
+  /**
+   * Gets a single element of the path.
+   */
+  std::string_view operator[](size_t n) const;
+
+  /**
+   * Returns a path reference with the first N elements of the path removed.
+   */
+  HttpPathRef drop_front(size_t n) const;
+
+ private:
+  SmallString<128> m_pathBuf;
+  SmallVector<size_t, 16> m_pathEnds;
+};
+
+/**
+ * Proxy reference object for a portion of a HttpPath.
+ */
+class HttpPathRef {
+ public:
+  HttpPathRef() = default;
+  /*implicit*/ HttpPathRef(const HttpPath& path,  // NOLINT
+                           size_t start = 0)
+      : m_path(&path), m_start(start) {}
+
+  explicit operator bool() const { return !empty(); }
+  bool empty() const { return m_path && m_path->size() == m_start; }
+  size_t size() const { return m_path ? m_path->size() - m_start : 0; }
+
+  bool equals(std::initializer_list<std::string_view> match) const {
+    return equals(0, {match.begin(), match.end()});
+  }
+  bool equals(std::span<const std::string_view> match) const {
+    return equals(0, match);
+  }
+  bool equals(std::string_view match) const { return equals(0, {match}); }
+
+  bool equals(size_t start,
+              std::initializer_list<std::string_view> match) const {
+    return equals(start, {match.begin(), match.end()});
+  }
+  bool equals(size_t start, std::span<const std::string_view> match) const {
+    return m_path ? m_path->equals(m_start + start, match) : false;
+  }
+  bool equals(size_t start, std::string_view match) const {
+    return equals(start, {match});
+  }
+
+  bool startswith(std::initializer_list<std::string_view> match) const {
+    return startswith(0, {match.begin(), match.end()});
+  }
+  bool startswith(std::span<const std::string_view> match) const {
+    return startswith(0, match);
+  }
+  bool startswith(std::string_view match) const {
+    return startswith(0, {match});
+  }
+
+  bool startswith(size_t start,
+                  std::initializer_list<std::string_view> match) const {
+    return startswith(start, {match.begin(), match.end()});
+  }
+  bool startswith(size_t start, std::span<const std::string_view> match) const {
+    return m_path ? m_path->startswith(m_start + start, match) : false;
+  }
+  bool startswith(size_t start, std::string_view match) const {
+    return startswith(start, {match});
+  }
+
+  std::string_view operator[](size_t n) const {
+    return m_path ? m_path->operator[](m_start + n) : std::string_view{};
+  }
+  HttpPathRef drop_front(size_t n) const {
+    return m_path ? m_path->drop_front(m_start + n) : HttpPathRef{};
+  }
+
+ private:
+  const HttpPath* m_path = nullptr;
+  size_t m_start = 0;
+};
+
+class HttpLocation {
+ public:
+  HttpLocation() = default;
+  HttpLocation(std::string_view url_, bool* error, std::string* errorMsg);
+
+  std::string url;       // retain copy
+  std::string user;      // unescaped
+  std::string password;  // unescaped
+  std::string host;
+  int port;
+  std::string path;  // escaped, not including leading '/'
+  std::vector<std::pair<std::string, std::string>> params;  // unescaped
+  std::string fragment;
+};
+
+class HttpRequest {
+ public:
+  HttpRequest() = default;
+
+  explicit HttpRequest(const HttpLocation& loc)
+      : host{loc.host}, port{loc.port} {
+    SetPath(loc.path, loc.params);
+    SetAuth(loc);
+  }
+
+  template <typename T>
+  HttpRequest(const HttpLocation& loc, const T& extraParams);
+
+  HttpRequest(const HttpLocation& loc, std::string_view path_)
+      : host{loc.host}, port{loc.port}, path{path_} {
+    SetAuth(loc);
+  }
+
+  template <typename T>
+  HttpRequest(const HttpLocation& loc, std::string_view path_, const T& params)
+      : host{loc.host}, port{loc.port} {
+    SetPath(path_, params);
+    SetAuth(loc);
+  }
+
+  SmallString<128> host;
+  int port;
+  std::string auth;
+  SmallString<128> path;
+
+ private:
+  void SetAuth(const HttpLocation& loc);
+  template <typename T>
+  void SetPath(std::string_view path_, const T& params);
+
+  template <typename T>
+  static std::string_view GetFirst(const T& elem) {
+    return elem.first;
+  }
+  template <typename T>
+  static std::string_view GetFirst(const StringMapEntry<T>& elem) {
+    return elem.getKey();
+  }
+  template <typename T>
+  static std::string_view GetSecond(const T& elem) {
+    return elem.second;
+  }
+};
+
+class HttpConnection {
+ public:
+  HttpConnection(std::unique_ptr<wpi::NetworkStream> stream_, int timeout)
+      : stream{std::move(stream_)}, is{*stream, timeout}, os{*stream, true} {}
+
+  bool Handshake(const HttpRequest& request, std::string* warnMsg);
+
+  std::unique_ptr<wpi::NetworkStream> stream;
+  wpi::raw_socket_istream is;
+  wpi::raw_socket_ostream os;
+
+  // Valid after Handshake() is successful
+  SmallString<64> contentType;
+  SmallString<64> contentLength;
+
+  explicit operator bool() const { return stream && !is.has_error(); }
+};
+
+class HttpMultipartScanner {
+ public:
+  explicit HttpMultipartScanner(std::string_view boundary,
+                                bool saveSkipped = false) {
+    Reset(saveSkipped);
+    SetBoundary(boundary);
+  }
+
+  // Change the boundary.  This is only safe to do when IsDone() is true (or
+  // immediately after construction).
+  void SetBoundary(std::string_view boundary);
+
+  // Reset the scanner.  This allows reuse of internal buffers.
+  void Reset(bool saveSkipped = false);
+
+  // Execute the scanner.  Will automatically call Reset() on entry if IsDone()
+  // is true.
+  // @param in input data
+  // @return the input not consumed; empty if all input consumed
+  std::string_view Execute(std::string_view in);
+
+  // Returns true when the boundary has been found.
+  bool IsDone() const { return m_state == kDone; }
+
+  // Get the skipped data.  Will be empty if saveSkipped was false.
+  std::string_view GetSkipped() const {
+    return m_saveSkipped ? std::string_view{m_buf} : std::string_view{};
+  }
+
+ private:
+  SmallString<64> m_boundaryWith, m_boundaryWithout;
+
+  // Internal state
+  enum State { kBoundary, kPadding, kDone };
+  State m_state;
+  size_t m_posWith, m_posWithout;
+  enum Dashes { kUnknown, kWith, kWithout };
+  Dashes m_dashes;
+
+  // Buffer
+  bool m_saveSkipped;
+  std::string m_buf;
+};
+
+}  // namespace wpi
+
+#include "HttpUtil.inc"
+
+#endif  // WPINET_HTTPUTIL_H_
diff --git a/wpinet/src/main/native/include/wpinet/HttpUtil.inc b/wpinet/src/main/native/include/wpinet/HttpUtil.inc
new file mode 100644
index 0000000..f70b8dd
--- /dev/null
+++ b/wpinet/src/main/native/include/wpinet/HttpUtil.inc
@@ -0,0 +1,55 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#ifndef WPINET_HTTPUTIL_INC_
+#define WPINET_HTTPUTIL_INC_
+
+#include <utility>
+
+#include "wpinet/HttpUtil.h"
+
+namespace wpi {
+
+inline HttpPathRef HttpPath::drop_front(size_t n) const {
+  return HttpPathRef(*this, n);
+}
+
+template <typename T>
+HttpRequest::HttpRequest(const HttpLocation& loc, const T& extraParams)
+    : host{loc.host}, port{loc.port} {
+  StringMap<std::string_view> params;
+  for (const auto& p : loc.params) {
+    params.insert(std::make_pair(GetFirst(p), GetSecond(p)));
+  }
+  for (const auto& p : extraParams) {
+    params.insert(std::make_pair(GetFirst(p), GetSecond(p)));
+  }
+  SetPath(loc.path, params);
+  SetAuth(loc);
+}
+
+template <typename T>
+void HttpRequest::SetPath(std::string_view path_, const T& params) {
+  // Build location including query string
+  raw_svector_ostream pathOs{path};
+  pathOs << path_;
+  bool first = true;
+  for (const auto& param : params) {
+    if (first) {
+      pathOs << '?';
+      first = false;
+    } else {
+      pathOs << '&';
+    }
+    SmallString<64> escapeBuf;
+    pathOs << EscapeURI(GetFirst(param), escapeBuf, false);
+    if (!GetSecond(param).empty()) {
+      pathOs << '=' << EscapeURI(GetSecond(param), escapeBuf, false);
+    }
+  }
+}
+
+}  // namespace wpi
+
+#endif  // WPINET_HTTPUTIL_INC_
diff --git a/wpinet/src/main/native/include/wpinet/HttpWebSocketServerConnection.h b/wpinet/src/main/native/include/wpinet/HttpWebSocketServerConnection.h
new file mode 100644
index 0000000..4095a9c
--- /dev/null
+++ b/wpinet/src/main/native/include/wpinet/HttpWebSocketServerConnection.h
@@ -0,0 +1,93 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#ifndef WPINET_HTTPWEBSOCKETSERVERCONNECTION_H_
+#define WPINET_HTTPWEBSOCKETSERVERCONNECTION_H_
+
+#include <initializer_list>
+#include <memory>
+#include <span>
+#include <string>
+#include <string_view>
+
+#include <wpi/SmallVector.h>
+
+#include "wpinet/HttpServerConnection.h"
+#include "wpinet/WebSocket.h"
+#include "wpinet/WebSocketServer.h"
+#include "wpinet/uv/Stream.h"
+
+namespace wpi {
+
+/**
+ * A server-side HTTP connection that also accepts WebSocket upgrades.
+ *
+ * @tparam Derived derived class for std::enable_shared_from_this.
+ */
+template <typename Derived>
+class HttpWebSocketServerConnection
+    : public HttpServerConnection,
+      public std::enable_shared_from_this<Derived> {
+ public:
+  /**
+   * Constructor.
+   *
+   * @param stream network stream
+   * @param protocols Acceptable subprotocols
+   */
+  HttpWebSocketServerConnection(std::shared_ptr<uv::Stream> stream,
+                                std::span<const std::string_view> protocols);
+
+  /**
+   * Constructor.
+   *
+   * @param stream network stream
+   * @param protocols Acceptable subprotocols
+   */
+  HttpWebSocketServerConnection(
+      std::shared_ptr<uv::Stream> stream,
+      std::initializer_list<std::string_view> protocols)
+      : HttpWebSocketServerConnection(stream,
+                                      {protocols.begin(), protocols.end()}) {}
+
+ protected:
+  /**
+   * Check that an incoming WebSocket upgrade is okay.  This is called prior
+   * to accepting the upgrade (so prior to ProcessWsUpgrade()).
+   *
+   * The implementation should check other headers and return true if the
+   * WebSocket connection should be accepted.
+   *
+   * @param protocol negotiated subprotocol
+   */
+  virtual bool IsValidWsUpgrade(std::string_view protocol) { return true; }
+
+  /**
+   * Process an incoming WebSocket upgrade.  This is called after the header
+   * reader has been disconnected and the websocket has been accepted.
+   *
+   * The implementation should set up appropriate callbacks on the websocket
+   * object to continue communication.
+   *
+   * @note When a WebSocket upgrade occurs, the stream user data is replaced
+   *       with the websocket, and the websocket user data points to "this".
+   *       Replace the websocket user data with caution!
+   */
+  virtual void ProcessWsUpgrade() = 0;
+
+  /**
+   * WebSocket connection; not valid until ProcessWsUpgrade is called.
+   */
+  WebSocket* m_websocket = nullptr;
+
+ private:
+  WebSocketServerHelper m_helper;
+  SmallVector<std::string, 2> m_protocols;
+};
+
+}  // namespace wpi
+
+#include "HttpWebSocketServerConnection.inc"
+
+#endif  // WPINET_HTTPWEBSOCKETSERVERCONNECTION_H_
diff --git a/wpinet/src/main/native/include/wpinet/HttpWebSocketServerConnection.inc b/wpinet/src/main/native/include/wpinet/HttpWebSocketServerConnection.inc
new file mode 100644
index 0000000..750439d
--- /dev/null
+++ b/wpinet/src/main/native/include/wpinet/HttpWebSocketServerConnection.inc
@@ -0,0 +1,57 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#ifndef WPINET_HTTPWEBSOCKETSERVERCONNECTION_INC_
+#define WPINET_HTTPWEBSOCKETSERVERCONNECTION_INC_
+
+#include <memory>
+
+#include "wpinet/HttpWebSocketServerConnection.h"
+
+namespace wpi {
+
+template <typename Derived>
+HttpWebSocketServerConnection<Derived>::HttpWebSocketServerConnection(
+    std::shared_ptr<uv::Stream> stream,
+    std::span<const std::string_view> protocols)
+    : HttpServerConnection{stream},
+      m_helper{m_request},
+      m_protocols{protocols.begin(), protocols.end()} {
+  // Handle upgrade event
+  m_helper.upgrade.connect([this] {
+    // Negotiate sub-protocol
+    SmallVector<std::string_view, 2> protocols{m_protocols.begin(),
+                                               m_protocols.end()};
+    std::string_view protocol = m_helper.MatchProtocol(protocols).second;
+
+    // Check that the upgrade is valid
+    if (!IsValidWsUpgrade(protocol)) {
+      return;
+    }
+
+    // Disconnect HttpServerConnection header reader
+    m_dataConn.disconnect();
+    m_messageCompleteConn.disconnect();
+
+    // Accepting the stream may destroy this (as it replaces the stream user
+    // data), so grab a shared pointer first.
+    auto self = this->shared_from_this();
+
+    // Accept the upgrade
+    auto ws = m_helper.Accept(m_stream, protocol);
+
+    // Set this as the websocket user data to keep it around
+    ws->SetData(self);
+
+    // Store in member
+    m_websocket = ws.get();
+
+    // Call derived class function
+    ProcessWsUpgrade();
+  });
+}
+
+}  // namespace wpi
+
+#endif  // WPINET_HTTPWEBSOCKETSERVERCONNECTION_INC_
diff --git a/wpinet/src/main/native/include/wpinet/MimeTypes.h b/wpinet/src/main/native/include/wpinet/MimeTypes.h
new file mode 100644
index 0000000..2f5fe72
--- /dev/null
+++ b/wpinet/src/main/native/include/wpinet/MimeTypes.h
@@ -0,0 +1,16 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#ifndef WPINET_MIMETYPES_H_
+#define WPINET_MIMETYPES_H_
+
+#include <string_view>
+
+namespace wpi {
+
+std::string_view MimeTypeFromPath(std::string_view path);
+
+}  // namespace wpi
+
+#endif  // WPINET_MIMETYPES_H_
diff --git a/wpinet/src/main/native/include/wpinet/MulticastServiceAnnouncer.h b/wpinet/src/main/native/include/wpinet/MulticastServiceAnnouncer.h
new file mode 100644
index 0000000..2892919
--- /dev/null
+++ b/wpinet/src/main/native/include/wpinet/MulticastServiceAnnouncer.h
@@ -0,0 +1,59 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#pragma once
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+#include <memory>
+#include <span>
+#include <string>
+#include <string_view>
+#include <utility>
+namespace wpi {
+class MulticastServiceAnnouncer {
+ public:
+  MulticastServiceAnnouncer(
+      std::string_view serviceName, std::string_view serviceType, int port,
+      std::span<const std::pair<std::string, std::string>> txt);
+  MulticastServiceAnnouncer(
+      std::string_view serviceName, std::string_view serviceType, int port,
+      std::span<const std::pair<std::string_view, std::string_view>> txt);
+  MulticastServiceAnnouncer(std::string_view serviceName,
+                            std::string_view serviceType, int port);
+  ~MulticastServiceAnnouncer() noexcept;
+  void Start();
+  void Stop();
+  bool HasImplementation() const;
+  struct Impl;
+
+ private:
+  std::unique_ptr<Impl> pImpl;
+};
+}  // namespace wpi
+extern "C" {
+#endif
+
+typedef unsigned int WPI_MulticastServiceAnnouncerHandle;  // NOLINT
+
+WPI_MulticastServiceAnnouncerHandle WPI_CreateMulticastServiceAnnouncer(
+    const char* serviceName, const char* serviceType, int32_t port,
+    int32_t txtCount, const char** keys, const char** values);
+
+void WPI_FreeMulticastServiceAnnouncer(
+    WPI_MulticastServiceAnnouncerHandle handle);
+
+void WPI_StartMulticastServiceAnnouncer(
+    WPI_MulticastServiceAnnouncerHandle handle);
+
+void WPI_StopMulticastServiceAnnouncer(
+    WPI_MulticastServiceAnnouncerHandle handle);
+
+int32_t WPI_GetMulticastServiceAnnouncerHasImplementation(
+    WPI_MulticastServiceAnnouncerHandle handle);
+
+#ifdef __cplusplus
+}  // extern "C"
+#endif
diff --git a/wpinet/src/main/native/include/wpinet/MulticastServiceResolver.h b/wpinet/src/main/native/include/wpinet/MulticastServiceResolver.h
new file mode 100644
index 0000000..f46a8e4
--- /dev/null
+++ b/wpinet/src/main/native/include/wpinet/MulticastServiceResolver.h
@@ -0,0 +1,98 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#pragma once
+
+#include <wpi/Synchronization.h>
+
+#ifdef __cplusplus
+#include <functional>
+#include <memory>
+#include <string>
+#include <string_view>
+#include <utility>
+#include <vector>
+
+#include <wpi/mutex.h>
+namespace wpi {
+class MulticastServiceResolver {
+ public:
+  explicit MulticastServiceResolver(std::string_view serviceType);
+  ~MulticastServiceResolver() noexcept;
+  struct ServiceData {
+    unsigned int ipv4Address;
+    int port;
+    std::string serviceName;
+    std::string hostName;
+    std::vector<std::pair<std::string, std::string>> txt;
+  };
+  void Start();
+  void Stop();
+  WPI_EventHandle GetEventHandle() const { return event.GetHandle(); }
+  std::vector<ServiceData> GetData() {
+    std::scoped_lock lock{mutex};
+    event.Reset();
+    if (queue.empty()) {
+      return {};
+    }
+    std::vector<ServiceData> ret;
+    queue.swap(ret);
+    return ret;
+  }
+  bool HasImplementation() const;
+  struct Impl;
+
+ private:
+  void PushData(ServiceData&& data) {
+    std::scoped_lock lock{mutex};
+    queue.emplace_back(std::forward<ServiceData>(data));
+    event.Set();
+  }
+  wpi::Event event{true};
+  std::vector<ServiceData> queue;
+  wpi::mutex mutex;
+  std::unique_ptr<Impl> pImpl;
+};
+}  // namespace wpi
+extern "C" {
+#endif
+
+typedef unsigned int WPI_MulticastServiceResolverHandle;  // NOLINT
+
+WPI_MulticastServiceResolverHandle WPI_CreateMulticastServiceResolver(
+    const char* serviceType);
+
+void WPI_FreeMulticastServiceResolver(
+    WPI_MulticastServiceResolverHandle handle);
+
+void WPI_StartMulticastServiceResolver(
+    WPI_MulticastServiceResolverHandle handle);
+
+void WPI_StopMulticastServiceResolver(
+    WPI_MulticastServiceResolverHandle handle);
+
+int32_t WPI_GetMulticastServiceResolverHasImplementation(
+    WPI_MulticastServiceResolverHandle handle);
+
+WPI_EventHandle WPI_GetMulticastServiceResolverEventHandle(
+    WPI_MulticastServiceResolverHandle handle);
+
+typedef struct WPI_ServiceData {  // NOLINT
+  uint32_t ipv4Address;
+  int32_t port;
+  const char* serviceName;
+  const char* hostName;
+  int32_t txtCount;
+  const char** txtKeys;
+  const char** txtValues;
+} WPI_ServiceData;
+
+WPI_ServiceData* WPI_GetMulticastServiceResolverData(
+    WPI_MulticastServiceResolverHandle handle, int32_t* dataCount);
+
+void WPI_FreeServiceData(WPI_ServiceData* serviceData, int32_t length);
+
+#ifdef __cplusplus
+}  // extern "C"
+#endif
diff --git a/wpinet/src/main/native/include/wpinet/NetworkAcceptor.h b/wpinet/src/main/native/include/wpinet/NetworkAcceptor.h
new file mode 100644
index 0000000..6fe2e3b
--- /dev/null
+++ b/wpinet/src/main/native/include/wpinet/NetworkAcceptor.h
@@ -0,0 +1,29 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#ifndef WPINET_NETWORKACCEPTOR_H_
+#define WPINET_NETWORKACCEPTOR_H_
+
+#include <memory>
+
+#include "wpinet/NetworkStream.h"
+
+namespace wpi {
+
+class NetworkAcceptor {
+ public:
+  NetworkAcceptor() = default;
+  virtual ~NetworkAcceptor() = default;
+
+  virtual int start() = 0;
+  virtual void shutdown() = 0;
+  virtual std::unique_ptr<NetworkStream> accept() = 0;
+
+  NetworkAcceptor(const NetworkAcceptor&) = delete;
+  NetworkAcceptor& operator=(const NetworkAcceptor&) = delete;
+};
+
+}  // namespace wpi
+
+#endif  // WPINET_NETWORKACCEPTOR_H_
diff --git a/wpinet/src/main/native/include/wpinet/NetworkStream.h b/wpinet/src/main/native/include/wpinet/NetworkStream.h
new file mode 100644
index 0000000..8329b88
--- /dev/null
+++ b/wpinet/src/main/native/include/wpinet/NetworkStream.h
@@ -0,0 +1,44 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#ifndef WPINET_NETWORKSTREAM_H_
+#define WPINET_NETWORKSTREAM_H_
+
+#include <cstddef>
+#include <string_view>
+
+namespace wpi {
+
+class NetworkStream {
+ public:
+  NetworkStream() = default;
+  virtual ~NetworkStream() = default;
+
+  enum Error {
+    kConnectionClosed = 0,
+    kConnectionReset = -1,
+    kConnectionTimedOut = -2,
+    kWouldBlock = -3
+  };
+
+  virtual size_t send(const char* buffer, size_t len, Error* err) = 0;
+  virtual size_t receive(char* buffer, size_t len, Error* err,
+                         int timeout = 0) = 0;
+  virtual void close() = 0;
+
+  virtual std::string_view getPeerIP() const = 0;
+  virtual int getPeerPort() const = 0;
+  virtual void setNoDelay() = 0;
+
+  // returns false on failure
+  virtual bool setBlocking(bool enabled) = 0;
+  virtual int getNativeHandle() const = 0;
+
+  NetworkStream(const NetworkStream&) = delete;
+  NetworkStream& operator=(const NetworkStream&) = delete;
+};
+
+}  // namespace wpi
+
+#endif  // WPINET_NETWORKSTREAM_H_
diff --git a/wpinet/src/main/native/include/wpinet/ParallelTcpConnector.h b/wpinet/src/main/native/include/wpinet/ParallelTcpConnector.h
new file mode 100644
index 0000000..e7bc953
--- /dev/null
+++ b/wpinet/src/main/native/include/wpinet/ParallelTcpConnector.h
@@ -0,0 +1,119 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#pragma once
+
+#include <stdint.h>
+
+#include <functional>
+#include <memory>
+#include <span>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "wpinet/uv/Timer.h"
+
+namespace wpi {
+
+class Logger;
+
+namespace uv {
+class GetAddrInfoReq;
+class Loop;
+class Tcp;
+class Timer;
+}  // namespace uv
+
+/**
+ * Parallel TCP connector.  Attempts parallel resolution and connection to
+ * multiple servers with automatic retry if none connect.
+ *
+ * Each successful TCP connection results in a call to the connected callback.
+ * For correct operation, the consuming code (either the connected callback or
+ * e.g. task it starts) must call Succeeded() to indicate if the connection has
+ * succeeded prior to the reconnect rate timeout.  A successful connection
+ * results in the connector terminating all other connection attempts.
+ *
+ * After the reconnect rate times out, all remaining active connection attempts
+ * are canceled and new ones started.
+ */
+class ParallelTcpConnector
+    : public std::enable_shared_from_this<ParallelTcpConnector> {
+  struct private_init {};
+
+ public:
+  /**
+   * Create.
+   *
+   * @param loop loop
+   * @param reconnectRate how long to wait after starting connection attempts
+   *                      to cancel and attempt connecting again
+   * @param logger logger
+   * @param connected callback function when a connection succeeds; may be
+   *                  called multiple times if it does not call Succeeded()
+   *                  before returning
+   * @return Parallel connector
+   */
+  static std::shared_ptr<ParallelTcpConnector> Create(
+      wpi::uv::Loop& loop, wpi::uv::Timer::Time reconnectRate,
+      wpi::Logger& logger, std::function<void(wpi::uv::Tcp& tcp)> connected) {
+    return std::make_shared<ParallelTcpConnector>(
+        loop, reconnectRate, logger, std::move(connected), private_init{});
+  }
+
+  ParallelTcpConnector(wpi::uv::Loop& loop, wpi::uv::Timer::Time reconnectRate,
+                       wpi::Logger& logger,
+                       std::function<void(wpi::uv::Tcp& tcp)> connected,
+                       const private_init&);
+  ~ParallelTcpConnector();
+
+  ParallelTcpConnector(const ParallelTcpConnector&) = delete;
+  ParallelTcpConnector& operator=(const ParallelTcpConnector&) = delete;
+
+  /**
+   * Closes resources, canceling all pending action attempts.
+   */
+  void Close();
+
+  /**
+   * Changes the servers/ports to connect to.  Starts connection attempts if not
+   * already connected.
+   *
+   * @param servers array of server/port pairs
+   */
+  void SetServers(
+      std::span<const std::pair<std::string, unsigned int>> servers);
+
+  /**
+   * Tells the parallel connector that the current connection has terminated and
+   * it is necessary to start reconnection attempts.
+   */
+  void Disconnected();
+
+  /**
+   * Tells the parallel connector that a particular connection has succeeded and
+   * it should stop trying to connect.
+   *
+   * @param tcp connection passed to connected callback
+   */
+  void Succeeded(wpi::uv::Tcp& tcp);
+
+ private:
+  bool IsConnected() const { return m_isConnected || m_servers.empty(); }
+  void Connect();
+  void CancelAll(wpi::uv::Tcp* except = nullptr);
+
+  wpi::uv::Loop& m_loop;
+  wpi::Logger& m_logger;
+  wpi::uv::Timer::Time m_reconnectRate;
+  std::function<void(wpi::uv::Tcp& tcp)> m_connected;
+  std::shared_ptr<wpi::uv::Timer> m_reconnectTimer;
+  std::vector<std::pair<std::string, unsigned int>> m_servers;
+  std::vector<std::weak_ptr<wpi::uv::GetAddrInfoReq>> m_resolvers;
+  std::vector<std::weak_ptr<wpi::uv::Tcp>> m_attempts;
+  bool m_isConnected{false};
+};
+
+}  // namespace wpi
diff --git a/wpinet/src/main/native/include/wpinet/PortForwarder.h b/wpinet/src/main/native/include/wpinet/PortForwarder.h
new file mode 100644
index 0000000..14453a3
--- /dev/null
+++ b/wpinet/src/main/native/include/wpinet/PortForwarder.h
@@ -0,0 +1,59 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#ifndef WPINET_PORTFORWARDER_H_
+#define WPINET_PORTFORWARDER_H_
+
+#pragma once
+
+#include <memory>
+#include <string_view>
+
+namespace wpi {
+
+/**
+ * Forward ports to another host.  This is primarily useful for accessing
+ * Ethernet-connected devices from a computer tethered to the RoboRIO USB port.
+ */
+class PortForwarder {
+ public:
+  PortForwarder(const PortForwarder&) = delete;
+  PortForwarder& operator=(const PortForwarder&) = delete;
+
+  /**
+   * Get an instance of the PortForwarder class.
+   *
+   * This is a singleton to guarantee that there is only a single instance
+   * regardless of how many times GetInstance is called.
+   */
+  static PortForwarder& GetInstance();
+
+  /**
+   * Forward a local TCP port to a remote host and port.
+   * Note that local ports less than 1024 won't work as a normal user.
+   *
+   * @param port       local port number
+   * @param remoteHost remote IP address / DNS name
+   * @param remotePort remote port number
+   */
+  void Add(unsigned int port, std::string_view remoteHost,
+           unsigned int remotePort);
+
+  /**
+   * Stop TCP forwarding on a port.
+   *
+   * @param port local port number
+   */
+  void Remove(unsigned int port);
+
+ private:
+  PortForwarder();
+
+  struct Impl;
+  std::unique_ptr<Impl> m_impl;
+};
+
+}  // namespace wpi
+
+#endif  // WPINET_PORTFORWARDER_H_
diff --git a/wpinet/src/main/native/include/wpinet/SocketError.h b/wpinet/src/main/native/include/wpinet/SocketError.h
new file mode 100644
index 0000000..c944b0c
--- /dev/null
+++ b/wpinet/src/main/native/include/wpinet/SocketError.h
@@ -0,0 +1,22 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#ifndef WPINET_SOCKETERROR_H_
+#define WPINET_SOCKETERROR_H_
+
+#include <string>
+
+namespace wpi {
+
+int SocketErrno();
+
+std::string SocketStrerror(int code);
+
+inline std::string SocketStrerror() {
+  return SocketStrerror(SocketErrno());
+}
+
+}  // namespace wpi
+
+#endif  // WPINET_SOCKETERROR_H_
diff --git a/wpinet/src/main/native/include/wpinet/UDPClient.h b/wpinet/src/main/native/include/wpinet/UDPClient.h
new file mode 100644
index 0000000..55e5702
--- /dev/null
+++ b/wpinet/src/main/native/include/wpinet/UDPClient.h
@@ -0,0 +1,49 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#ifndef WPINET_UDPCLIENT_H_
+#define WPINET_UDPCLIENT_H_
+
+#include <span>
+#include <string>
+#include <string_view>
+
+#include <wpi/SmallVector.h>
+#include <wpi/mutex.h>
+
+namespace wpi {
+
+class Logger;
+
+class UDPClient {
+  int m_lsd;
+  int m_port;
+  std::string m_address;
+  Logger& m_logger;
+
+ public:
+  explicit UDPClient(Logger& logger);
+  UDPClient(std::string_view address, Logger& logger);
+  UDPClient(const UDPClient& other) = delete;
+  UDPClient(UDPClient&& other);
+  ~UDPClient();
+
+  UDPClient& operator=(const UDPClient& other) = delete;
+  UDPClient& operator=(UDPClient&& other);
+
+  int start();
+  int start(int port);
+  void shutdown();
+  // The passed in address MUST be a resolved IP address.
+  int send(std::span<const uint8_t> data, std::string_view server, int port);
+  int send(std::string_view data, std::string_view server, int port);
+  int receive(uint8_t* data_received, int receive_len);
+  int receive(uint8_t* data_received, int receive_len,
+              SmallVectorImpl<char>* addr_received, int* port_received);
+  int set_timeout(double timeout);
+};
+
+}  // namespace wpi
+
+#endif  // WPINET_UDPCLIENT_H_
diff --git a/wpinet/src/main/native/include/wpinet/UrlParser.h b/wpinet/src/main/native/include/wpinet/UrlParser.h
new file mode 100644
index 0000000..92fd330
--- /dev/null
+++ b/wpinet/src/main/native/include/wpinet/UrlParser.h
@@ -0,0 +1,96 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#ifndef WPINET_URLPARSER_H_
+#define WPINET_URLPARSER_H_
+
+#include <string_view>
+
+#include <wpi/StringExtras.h>
+
+#include "wpinet/http_parser.h"
+
+namespace wpi {
+
+/**
+ * Parses a URL into its constiuent components.
+ * `schema://userinfo@host:port/the/path?query#fragment`
+ */
+class UrlParser {
+ public:
+  /**
+   * Parse a URL.
+   * @param in input
+   * @param isConnect
+   */
+  UrlParser(std::string_view in, bool isConnect) {
+    m_data = in;
+    http_parser_url_init(&m_url);
+    m_error = http_parser_parse_url(in.data(), in.size(), isConnect, &m_url);
+  }
+
+  /**
+   * Determine if the URL is valid (e.g. the parse was successful).
+   */
+  bool IsValid() const { return !m_error; }
+
+  bool HasSchema() const { return (m_url.field_set & (1 << UF_SCHEMA)) != 0; }
+
+  bool HasHost() const { return (m_url.field_set & (1 << UF_HOST)) != 0; }
+
+  bool HasPort() const { return (m_url.field_set & (1 << UF_PORT)) != 0; }
+
+  bool HasPath() const { return (m_url.field_set & (1 << UF_PATH)) != 0; }
+
+  bool HasQuery() const { return (m_url.field_set & (1 << UF_QUERY)) != 0; }
+
+  bool HasFragment() const {
+    return (m_url.field_set & (1 << UF_FRAGMENT)) != 0;
+  }
+
+  bool HasUserInfo() const {
+    return (m_url.field_set & (1 << UF_USERINFO)) != 0;
+  }
+
+  std::string_view GetSchema() const {
+    return wpi::substr(m_data, m_url.field_data[UF_SCHEMA].off,
+                       m_url.field_data[UF_SCHEMA].len);
+  }
+
+  std::string_view GetHost() const {
+    return wpi::substr(m_data, m_url.field_data[UF_HOST].off,
+                       m_url.field_data[UF_HOST].len);
+  }
+
+  unsigned int GetPort() const { return m_url.port; }
+
+  std::string_view GetPath() const {
+    return wpi::substr(m_data, m_url.field_data[UF_PATH].off,
+                       m_url.field_data[UF_PATH].len);
+  }
+
+  std::string_view GetQuery() const {
+    return wpi::substr(m_data, m_url.field_data[UF_QUERY].off,
+                       m_url.field_data[UF_QUERY].len);
+  }
+
+  std::string_view GetFragment() const {
+    return wpi::substr(m_data, m_url.field_data[UF_FRAGMENT].off,
+                       m_url.field_data[UF_FRAGMENT].len);
+  }
+
+  std::string_view GetUserInfo() const {
+    return wpi::substr(m_data, m_url.field_data[UF_USERINFO].off,
+                       m_url.field_data[UF_USERINFO].len);
+  }
+
+ private:
+  bool m_error;
+  std::string_view m_data;
+  http_parser_url m_url;
+};
+
+}  // namespace wpi
+
+#endif  // WPINET_URLPARSER_H_
diff --git a/wpinet/src/main/native/include/wpinet/WebSocket.h b/wpinet/src/main/native/include/wpinet/WebSocket.h
new file mode 100644
index 0000000..1f295c9
--- /dev/null
+++ b/wpinet/src/main/native/include/wpinet/WebSocket.h
@@ -0,0 +1,518 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#ifndef WPINET_WEBSOCKET_H_
+#define WPINET_WEBSOCKET_H_
+
+#include <stdint.h>
+
+#include <functional>
+#include <initializer_list>
+#include <memory>
+#include <span>
+#include <string>
+#include <string_view>
+#include <utility>
+
+#include <wpi/Signal.h>
+#include <wpi/SmallVector.h>
+
+#include "wpinet/uv/Buffer.h"
+#include "wpinet/uv/Error.h"
+#include "wpinet/uv/Timer.h"
+
+namespace wpi {
+
+namespace uv {
+class Stream;
+}  // namespace uv
+
+/**
+ * RFC 6455 compliant WebSocket client and server implementation.
+ */
+class WebSocket : public std::enable_shared_from_this<WebSocket> {
+  struct private_init {};
+
+  static constexpr uint8_t kOpCont = 0x00;
+  static constexpr uint8_t kOpText = 0x01;
+  static constexpr uint8_t kOpBinary = 0x02;
+  static constexpr uint8_t kOpClose = 0x08;
+  static constexpr uint8_t kOpPing = 0x09;
+  static constexpr uint8_t kOpPong = 0x0A;
+  static constexpr uint8_t kOpMask = 0x0F;
+  static constexpr uint8_t kFlagFin = 0x80;
+
+ public:
+  WebSocket(uv::Stream& stream, bool server, const private_init&);
+  WebSocket(const WebSocket&) = delete;
+  WebSocket(WebSocket&&) = delete;
+  WebSocket& operator=(const WebSocket&) = delete;
+  WebSocket& operator=(WebSocket&&) = delete;
+  ~WebSocket();
+
+  /**
+   * Connection states.
+   */
+  enum State {
+    /** The connection is not yet open. */
+    CONNECTING = 0,
+    /** The connection is open and ready to communicate. */
+    OPEN,
+    /** The connection is in the process of closing. */
+    CLOSING,
+    /** The connection failed. */
+    FAILED,
+    /** The connection is closed. */
+    CLOSED
+  };
+
+  /**
+   * Client connection options.
+   */
+  struct ClientOptions {
+    ClientOptions() : handshakeTimeout{(uv::Timer::Time::max)()} {}
+
+    /** Timeout for the handshake request. */
+    uv::Timer::Time handshakeTimeout;  // NOLINT
+
+    /** Additional headers to include in handshake. */
+    std::span<const std::pair<std::string_view, std::string_view>> extraHeaders;
+  };
+
+  /**
+   * Frame.  Used by SendFrames().
+   */
+  struct Frame {
+    static constexpr uint8_t kText = kFlagFin | kOpText;
+    static constexpr uint8_t kBinary = kFlagFin | kOpBinary;
+    static constexpr uint8_t kTextFragment = kOpText;
+    static constexpr uint8_t kBinaryFragment = kOpBinary;
+    static constexpr uint8_t kFragment = kOpCont;
+    static constexpr uint8_t kFinalFragment = kFlagFin | kOpCont;
+    static constexpr uint8_t kPing = kFlagFin | kOpPing;
+    static constexpr uint8_t kPong = kFlagFin | kOpPong;
+
+    Frame(uint8_t opcode, std::span<const uv::Buffer> data)
+        : opcode{opcode}, data{data} {}
+
+    uint8_t opcode;
+    std::span<const uv::Buffer> data;
+  };
+
+  /**
+   * Starts a client connection by performing the initial client handshake.
+   * An open event is emitted when the handshake completes.
+   * This sets the stream user data to the websocket.
+   * @param stream Connection stream
+   * @param uri The Request-URI to send
+   * @param host The host or host:port to send
+   * @param protocols The list of subprotocols
+   * @param options Handshake options
+   */
+  static std::shared_ptr<WebSocket> CreateClient(
+      uv::Stream& stream, std::string_view uri, std::string_view host,
+      std::span<const std::string_view> protocols = {},
+      const ClientOptions& options = {});
+
+  /**
+   * Starts a client connection by performing the initial client handshake.
+   * An open event is emitted when the handshake completes.
+   * This sets the stream user data to the websocket.
+   * @param stream Connection stream
+   * @param uri The Request-URI to send
+   * @param host The host or host:port to send
+   * @param protocols The list of subprotocols
+   * @param options Handshake options
+   */
+  static std::shared_ptr<WebSocket> CreateClient(
+      uv::Stream& stream, std::string_view uri, std::string_view host,
+      std::initializer_list<std::string_view> protocols,
+      const ClientOptions& options = {}) {
+    return CreateClient(stream, uri, host, {protocols.begin(), protocols.end()},
+                        options);
+  }
+
+  /**
+   * Starts a server connection by performing the initial server side handshake.
+   * This should be called after the HTTP headers have been received.
+   * An open event is emitted when the handshake completes.
+   * This sets the stream user data to the websocket.
+   * @param stream Connection stream
+   * @param key The value of the Sec-WebSocket-Key header field in the client
+   *            request
+   * @param version The value of the Sec-WebSocket-Version header field in the
+   *                client request
+   * @param protocol The subprotocol to send to the client (in the
+   *                 Sec-WebSocket-Protocol header field).
+   */
+  static std::shared_ptr<WebSocket> CreateServer(
+      uv::Stream& stream, std::string_view key, std::string_view version,
+      std::string_view protocol = {});
+
+  /**
+   * Get connection state.
+   */
+  State GetState() const { return m_state; }
+
+  /**
+   * Return if the connection is open.  Messages can only be sent on open
+   * connections.
+   */
+  bool IsOpen() const { return m_state == OPEN; }
+
+  /**
+   * Get the underlying stream.
+   */
+  uv::Stream& GetStream() const { return m_stream; }
+
+  /**
+   * Get the selected sub-protocol.  Only valid in or after the open() event.
+   */
+  std::string_view GetProtocol() const { return m_protocol; }
+
+  /**
+   * Set the maximum message size.  Default is 128 KB.  If configured to combine
+   * fragments this maximum applies to the entire message (all combined
+   * fragments).
+   * @param size Maximum message size in bytes
+   */
+  void SetMaxMessageSize(size_t size) { m_maxMessageSize = size; }
+
+  /**
+   * Set whether or not fragmented frames should be combined.  Default is to
+   * combine.  If fragmented frames are combined, the text and binary callbacks
+   * will always have the second parameter (fin) set to true.
+   * @param combine True if fragmented frames should be combined.
+   */
+  void SetCombineFragments(bool combine) { m_combineFragments = combine; }
+
+  /**
+   * Initiate a closing handshake.
+   * @param code A numeric status code (defaults to 1005, no status code)
+   * @param reason A human-readable string explaining why the connection is
+   *               closing (optional).
+   */
+  void Close(uint16_t code = 1005, std::string_view reason = {});
+
+  /**
+   * Send a text message.
+   * @param data UTF-8 encoded data to send
+   * @param callback Callback which is invoked when the write completes.
+   */
+  void SendText(
+      std::span<const uv::Buffer> data,
+      std::function<void(std::span<uv::Buffer>, uv::Error)> callback) {
+    Send(kFlagFin | kOpText, data, std::move(callback));
+  }
+
+  /**
+   * Send a text message.
+   * @param data UTF-8 encoded data to send
+   * @param callback Callback which is invoked when the write completes.
+   */
+  void SendText(
+      std::initializer_list<uv::Buffer> data,
+      std::function<void(std::span<uv::Buffer>, uv::Error)> callback) {
+    SendText({data.begin(), data.end()}, std::move(callback));
+  }
+
+  /**
+   * Send a binary message.
+   * @param data Data to send
+   * @param callback Callback which is invoked when the write completes.
+   */
+  void SendBinary(
+      std::span<const uv::Buffer> data,
+      std::function<void(std::span<uv::Buffer>, uv::Error)> callback) {
+    Send(kFlagFin | kOpBinary, data, std::move(callback));
+  }
+
+  /**
+   * Send a binary message.
+   * @param data Data to send
+   * @param callback Callback which is invoked when the write completes.
+   */
+  void SendBinary(
+      std::initializer_list<uv::Buffer> data,
+      std::function<void(std::span<uv::Buffer>, uv::Error)> callback) {
+    SendBinary({data.begin(), data.end()}, std::move(callback));
+  }
+
+  /**
+   * Send a text message fragment.  This must be followed by one or more
+   * SendFragment() calls, where the last one has fin=True, to complete the
+   * message.
+   * @param data UTF-8 encoded data to send
+   * @param callback Callback which is invoked when the write completes.
+   */
+  void SendTextFragment(
+      std::span<const uv::Buffer> data,
+      std::function<void(std::span<uv::Buffer>, uv::Error)> callback) {
+    Send(kOpText, data, std::move(callback));
+  }
+
+  /**
+   * Send a text message fragment.  This must be followed by one or more
+   * SendFragment() calls, where the last one has fin=True, to complete the
+   * message.
+   * @param data UTF-8 encoded data to send
+   * @param callback Callback which is invoked when the write completes.
+   */
+  void SendTextFragment(
+      std::initializer_list<uv::Buffer> data,
+      std::function<void(std::span<uv::Buffer>, uv::Error)> callback) {
+    SendTextFragment({data.begin(), data.end()}, std::move(callback));
+  }
+
+  /**
+   * Send a text message fragment.  This must be followed by one or more
+   * SendFragment() calls, where the last one has fin=True, to complete the
+   * message.
+   * @param data Data to send
+   * @param callback Callback which is invoked when the write completes.
+   */
+  void SendBinaryFragment(
+      std::span<const uv::Buffer> data,
+      std::function<void(std::span<uv::Buffer>, uv::Error)> callback) {
+    Send(kOpBinary, data, std::move(callback));
+  }
+
+  /**
+   * Send a text message fragment.  This must be followed by one or more
+   * SendFragment() calls, where the last one has fin=True, to complete the
+   * message.
+   * @param data Data to send
+   * @param callback Callback which is invoked when the write completes.
+   */
+  void SendBinaryFragment(
+      std::initializer_list<uv::Buffer> data,
+      std::function<void(std::span<uv::Buffer>, uv::Error)> callback) {
+    SendBinaryFragment({data.begin(), data.end()}, std::move(callback));
+  }
+
+  /**
+   * Send a continuation frame.  This is used to send additional parts of a
+   * message started with SendTextFragment() or SendBinaryFragment().
+   * @param data Data to send
+   * @param fin Set to true if this is the final fragment of the message
+   * @param callback Callback which is invoked when the write completes.
+   */
+  void SendFragment(
+      std::span<const uv::Buffer> data, bool fin,
+      std::function<void(std::span<uv::Buffer>, uv::Error)> callback) {
+    Send(kOpCont | (fin ? kFlagFin : 0), data, std::move(callback));
+  }
+
+  /**
+   * Send a continuation frame.  This is used to send additional parts of a
+   * message started with SendTextFragment() or SendBinaryFragment().
+   * @param data Data to send
+   * @param fin Set to true if this is the final fragment of the message
+   * @param callback Callback which is invoked when the write completes.
+   */
+  void SendFragment(
+      std::initializer_list<uv::Buffer> data, bool fin,
+      std::function<void(std::span<uv::Buffer>, uv::Error)> callback) {
+    SendFragment({data.begin(), data.end()}, fin, std::move(callback));
+  }
+
+  /**
+   * Send a ping frame with no data.
+   * @param callback Optional callback which is invoked when the ping frame
+   *                 write completes.
+   */
+  void SendPing(std::function<void(uv::Error)> callback = nullptr) {
+    SendPing({}, [f = std::move(callback)](auto bufs, uv::Error err) {
+      if (f) {
+        f(err);
+      }
+    });
+  }
+
+  /**
+   * Send a ping frame.
+   * @param data Data to send in the ping frame
+   * @param callback Callback which is invoked when the ping frame
+   *                 write completes.
+   */
+  void SendPing(
+      std::span<const uv::Buffer> data,
+      std::function<void(std::span<uv::Buffer>, uv::Error)> callback) {
+    Send(kFlagFin | kOpPing, data, std::move(callback));
+  }
+
+  /**
+   * Send a ping frame.
+   * @param data Data to send in the ping frame
+   * @param callback Callback which is invoked when the ping frame
+   *                 write completes.
+   */
+  void SendPing(
+      std::initializer_list<uv::Buffer> data,
+      std::function<void(std::span<uv::Buffer>, uv::Error)> callback) {
+    SendPing({data.begin(), data.end()}, std::move(callback));
+  }
+
+  /**
+   * Send a pong frame with no data.
+   * @param callback Optional callback which is invoked when the pong frame
+   *                 write completes.
+   */
+  void SendPong(std::function<void(uv::Error)> callback = nullptr) {
+    SendPong({}, [f = std::move(callback)](auto bufs, uv::Error err) {
+      if (f) {
+        f(err);
+      }
+    });
+  }
+
+  /**
+   * Send a pong frame.
+   * @param data Data to send in the pong frame
+   * @param callback Callback which is invoked when the pong frame
+   *                 write completes.
+   */
+  void SendPong(
+      std::span<const uv::Buffer> data,
+      std::function<void(std::span<uv::Buffer>, uv::Error)> callback) {
+    Send(kFlagFin | kOpPong, data, std::move(callback));
+  }
+
+  /**
+   * Send a pong frame.
+   * @param data Data to send in the pong frame
+   * @param callback Callback which is invoked when the pong frame
+   *                 write completes.
+   */
+  void SendPong(
+      std::initializer_list<uv::Buffer> data,
+      std::function<void(std::span<uv::Buffer>, uv::Error)> callback) {
+    SendPong({data.begin(), data.end()}, std::move(callback));
+  }
+
+  /**
+   * Send multiple frames.
+   *
+   * @param frames Frame type/data pairs
+   * @param callback Callback which is invoked when the write completes.
+   */
+  void SendFrames(
+      std::span<const Frame> frames,
+      std::function<void(std::span<uv::Buffer>, uv::Error)> callback);
+
+  /**
+   * Fail the connection.
+   */
+  void Fail(uint16_t code = 1002, std::string_view reason = "protocol error");
+
+  /**
+   * Forcibly close the connection.
+   */
+  void Terminate(uint16_t code = 1006, std::string_view reason = "terminated");
+
+  /**
+   * Gets user-defined data.
+   * @return User-defined data if any, nullptr otherwise.
+   */
+  template <typename T = void>
+  std::shared_ptr<T> GetData() const {
+    return std::static_pointer_cast<T>(m_data);
+  }
+
+  /**
+   * Sets user-defined data.
+   * @param data User-defined arbitrary data.
+   */
+  void SetData(std::shared_ptr<void> data) { m_data = std::move(data); }
+
+  /**
+   * Shuts down and closes the underlying stream.
+   */
+  void Shutdown();
+
+  /**
+   * Open event.  Emitted when the connection is open and ready to communicate.
+   * The parameter is the selected subprotocol.
+   */
+  sig::Signal<std::string_view> open;
+
+  /**
+   * Close event.  Emitted when the connection is closed.  The first parameter
+   * is a numeric value indicating the status code explaining why the connection
+   * has been closed.  The second parameter is a human-readable string
+   * explaining the reason why the connection has been closed.
+   */
+  sig::Signal<uint16_t, std::string_view> closed;
+
+  /**
+   * Text message event.  Emitted when a text message is received.
+   * The first parameter is the data, the second parameter is true if the
+   * data is the last fragment of the message.
+   */
+  sig::Signal<std::string_view, bool> text;
+
+  /**
+   * Binary message event.  Emitted when a binary message is received.
+   * The first parameter is the data, the second parameter is true if the
+   * data is the last fragment of the message.
+   */
+  sig::Signal<std::span<const uint8_t>, bool> binary;
+
+  /**
+   * Ping event.  Emitted when a ping message is received.
+   */
+  sig::Signal<std::span<const uint8_t>> ping;
+
+  /**
+   * Pong event.  Emitted when a pong message is received.
+   */
+  sig::Signal<std::span<const uint8_t>> pong;
+
+ private:
+  // user data
+  std::shared_ptr<void> m_data;
+
+  // constructor parameters
+  uv::Stream& m_stream;
+  bool m_server;
+
+  // subprotocol, set via constructor (server) or handshake (client)
+  std::string m_protocol;
+
+  // user-settable configuration
+  size_t m_maxMessageSize = 128 * 1024;
+  bool m_combineFragments = true;
+
+  // operating state
+  State m_state = CONNECTING;
+
+  // incoming message buffers/state
+  SmallVector<uint8_t, 14> m_header;
+  size_t m_headerSize = 0;
+  SmallVector<uint8_t, 1024> m_payload;
+  size_t m_frameStart = 0;
+  uint64_t m_frameSize = UINT64_MAX;
+  uint8_t m_fragmentOpcode = 0;
+
+  // temporary data used only during client handshake
+  class ClientHandshakeData;
+  std::unique_ptr<ClientHandshakeData> m_clientHandshake;
+
+  void StartClient(std::string_view uri, std::string_view host,
+                   std::span<const std::string_view> protocols,
+                   const ClientOptions& options);
+  void StartServer(std::string_view key, std::string_view version,
+                   std::string_view protocol);
+  void SendClose(uint16_t code, std::string_view reason);
+  void SetClosed(uint16_t code, std::string_view reason, bool failed = false);
+  void HandleIncoming(uv::Buffer& buf, size_t size);
+  void Send(uint8_t opcode, std::span<const uv::Buffer> data,
+            std::function<void(std::span<uv::Buffer>, uv::Error)> callback) {
+    SendFrames({{Frame{opcode, data}}}, std::move(callback));
+  }
+};
+
+}  // namespace wpi
+
+#endif  // WPINET_WEBSOCKET_H_
diff --git a/wpinet/src/main/native/include/wpinet/WebSocketServer.h b/wpinet/src/main/native/include/wpinet/WebSocketServer.h
new file mode 100644
index 0000000..9d655d3
--- /dev/null
+++ b/wpinet/src/main/native/include/wpinet/WebSocketServer.h
@@ -0,0 +1,179 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#ifndef WPINET_WEBSOCKETSERVER_H_
+#define WPINET_WEBSOCKETSERVER_H_
+
+#include <functional>
+#include <initializer_list>
+#include <memory>
+#include <span>
+#include <string>
+#include <string_view>
+#include <utility>
+
+#include <wpi/Signal.h>
+#include <wpi/SmallString.h>
+#include <wpi/SmallVector.h>
+
+#include "wpinet/HttpParser.h"
+#include "wpinet/WebSocket.h"
+
+namespace wpi {
+
+namespace uv {
+class Stream;
+}  // namespace uv
+
+/**
+ * WebSocket HTTP server helper.  Handles websocket-specific headers.  User
+ * must provide the HttpParser.
+ */
+class WebSocketServerHelper {
+ public:
+  /**
+   * Constructor.
+   * @param req HttpParser for request
+   */
+  explicit WebSocketServerHelper(HttpParser& req);
+
+  /**
+   * Get whether or not this was a websocket upgrade.
+   * Only valid during and after the upgrade event.
+   */
+  bool IsWebsocket() const { return m_websocket; }
+
+  /**
+   * Try to find a match to the list of sub-protocols provided by the client.
+   * The list is priority ordered, so the first match wins.
+   * Only valid during and after the upgrade event.
+   * @param protocols Acceptable protocols
+   * @return Pair; first item is true if a match was made, false if not.
+   *         Second item is the matched protocol if a match was made, otherwise
+   *         is empty.
+   */
+  std::pair<bool, std::string_view> MatchProtocol(
+      std::span<const std::string_view> protocols);
+
+  /**
+   * Try to find a match to the list of sub-protocols provided by the client.
+   * The list is priority ordered, so the first match wins.
+   * Only valid during and after the upgrade event.
+   * @param protocols Acceptable protocols
+   * @return Pair; first item is true if a match was made, false if not.
+   *         Second item is the matched protocol if a match was made, otherwise
+   *         is empty.
+   */
+  std::pair<bool, std::string_view> MatchProtocol(
+      std::initializer_list<std::string_view> protocols) {
+    return MatchProtocol({protocols.begin(), protocols.end()});
+  }
+
+  /**
+   * Accept the upgrade.  Disconnect other readers (such as the HttpParser
+   * reader) before calling this.  See also WebSocket::CreateServer().
+   * @param stream Connection stream
+   * @param protocol The subprotocol to send to the client
+   */
+  std::shared_ptr<WebSocket> Accept(uv::Stream& stream,
+                                    std::string_view protocol = {}) {
+    return WebSocket::CreateServer(stream, m_key, m_version, protocol);
+  }
+
+  bool IsUpgrade() const { return m_gotHost && m_websocket; }
+
+  /**
+   * Upgrade event.  Call Accept() to accept the upgrade.
+   */
+  sig::Signal<> upgrade;
+
+ private:
+  bool m_gotHost = false;
+  bool m_websocket = false;
+  SmallVector<std::string, 2> m_protocols;
+  SmallString<64> m_key;
+  SmallString<16> m_version;
+};
+
+/**
+ * Dedicated WebSocket server.
+ */
+class WebSocketServer : public std::enable_shared_from_this<WebSocketServer> {
+  struct private_init {};
+
+ public:
+  /**
+   * Server options.
+   */
+  struct ServerOptions {
+    /**
+     * Checker for URL.  Return true if URL should be accepted.  By default all
+     * URLs are accepted.
+     */
+    std::function<bool(std::string_view)> checkUrl;
+
+    /**
+     * Checker for Host header.  Return true if Host should be accepted.  By
+     * default all hosts are accepted.
+     */
+    std::function<bool(std::string_view)> checkHost;
+  };
+
+  /**
+   * Private constructor.
+   */
+  WebSocketServer(uv::Stream& stream,
+                  std::span<const std::string_view> protocols,
+                  ServerOptions options, const private_init&);
+
+  /**
+   * Starts a dedicated WebSocket server on the provided connection.  The
+   * connection should be an accepted client stream.
+   * This also sets the stream user data to the socket server.
+   * A connected event is emitted when the connection is opened.
+   * @param stream Connection stream
+   * @param protocols Acceptable subprotocols
+   * @param options Handshake options
+   */
+  static std::shared_ptr<WebSocketServer> Create(
+      uv::Stream& stream, std::span<const std::string_view> protocols = {},
+      const ServerOptions& options = {});
+
+  /**
+   * Starts a dedicated WebSocket server on the provided connection.  The
+   * connection should be an accepted client stream.
+   * This also sets the stream user data to the socket server.
+   * A connected event is emitted when the connection is opened.
+   * @param stream Connection stream
+   * @param protocols Acceptable subprotocols
+   * @param options Handshake options
+   */
+  static std::shared_ptr<WebSocketServer> Create(
+      uv::Stream& stream, std::initializer_list<std::string_view> protocols,
+      const ServerOptions& options = {}) {
+    return Create(stream, {protocols.begin(), protocols.end()}, options);
+  }
+
+  /**
+   * Connected event.  First parameter is the URL, second is the websocket.
+   */
+  sig::Signal<std::string_view, WebSocket&> connected;
+
+ private:
+  uv::Stream& m_stream;
+  HttpParser m_req{HttpParser::kRequest};
+  WebSocketServerHelper m_helper;
+  SmallVector<std::string, 2> m_protocols;
+  ServerOptions m_options;
+  bool m_aborted = false;
+  sig::ScopedConnection m_dataConn;
+  sig::ScopedConnection m_errorConn;
+  sig::ScopedConnection m_endConn;
+
+  void Abort(uint16_t code, std::string_view reason);
+};
+
+}  // namespace wpi
+
+#endif  // WPINET_WEBSOCKETSERVER_H_
diff --git a/wpinet/src/main/native/include/wpinet/WorkerThread.h b/wpinet/src/main/native/include/wpinet/WorkerThread.h
new file mode 100644
index 0000000..b5438cc
--- /dev/null
+++ b/wpinet/src/main/native/include/wpinet/WorkerThread.h
@@ -0,0 +1,286 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#ifndef WPINET_WORKERTHREAD_H_
+#define WPINET_WORKERTHREAD_H_
+
+#include <functional>
+#include <memory>
+#include <tuple>
+#include <utility>
+#include <vector>
+
+#include <wpi/SafeThread.h>
+#include <wpi/future.h>
+
+#include "wpinet/uv/Async.h"
+
+namespace wpi {
+
+namespace detail {
+
+template <typename R>
+struct WorkerThreadAsync {
+  using AfterWorkFunction = std::function<void(R)>;
+
+  ~WorkerThreadAsync() { UnsetLoop(); }
+
+  void SetLoop(uv::Loop& loop) {
+    auto async = uv::Async<AfterWorkFunction, R>::Create(loop);
+    async->wakeup.connect(
+        [](AfterWorkFunction func, R result) { func(result); });
+    m_async = async;
+  }
+
+  void UnsetLoop() {
+    if (auto async = m_async.lock()) {
+      async->Close();
+      m_async.reset();
+    }
+  }
+
+  std::weak_ptr<uv::Async<AfterWorkFunction, R>> m_async;
+};
+
+template <>
+struct WorkerThreadAsync<void> {
+  using AfterWorkFunction = std::function<void()>;
+
+  ~WorkerThreadAsync() { RemoveLoop(); }
+
+  void SetLoop(uv::Loop& loop) {
+    auto async = uv::Async<AfterWorkFunction>::Create(loop);
+    async->wakeup.connect([](AfterWorkFunction func) { func(); });
+    m_async = async;
+  }
+
+  void RemoveLoop() {
+    if (auto async = m_async.lock()) {
+      async->Close();
+      m_async.reset();
+    }
+  }
+
+  std::weak_ptr<uv::Async<AfterWorkFunction>> m_async;
+};
+
+template <typename R, typename... T>
+struct WorkerThreadRequest {
+  using WorkFunction = std::function<R(T...)>;
+  using AfterWorkFunction = typename WorkerThreadAsync<R>::AfterWorkFunction;
+
+  WorkerThreadRequest() = default;
+  WorkerThreadRequest(uint64_t promiseId_, WorkFunction work_,
+                      std::tuple<T...> params_)
+      : promiseId(promiseId_),
+        work(std::move(work_)),
+        params(std::move(params_)) {}
+  WorkerThreadRequest(WorkFunction work_, AfterWorkFunction afterWork_,
+                      std::tuple<T...> params_)
+      : promiseId(0),
+        work(std::move(work_)),
+        afterWork(std::move(afterWork_)),
+        params(std::move(params_)) {}
+
+  uint64_t promiseId;
+  WorkFunction work;
+  AfterWorkFunction afterWork;
+  std::tuple<T...> params;
+};
+
+template <typename R, typename... T>
+class WorkerThreadThread : public SafeThread {
+ public:
+  using Request = WorkerThreadRequest<R, T...>;
+
+  void Main() override;
+
+  std::vector<Request> m_requests;
+  PromiseFactory<R> m_promises;
+  detail::WorkerThreadAsync<R> m_async;
+};
+
+template <typename R, typename... T>
+void RunWorkerThreadRequest(WorkerThreadThread<R, T...>& thr,
+                            WorkerThreadRequest<R, T...>& req) {
+  R result = std::apply(req.work, std::move(req.params));
+  if (req.afterWork) {
+    if (auto async = thr.m_async.m_async.lock()) {
+      async->Send(std::move(req.afterWork), std::move(result));
+    }
+  } else {
+    thr.m_promises.SetValue(req.promiseId, std::move(result));
+  }
+}
+
+template <typename... T>
+void RunWorkerThreadRequest(WorkerThreadThread<void, T...>& thr,
+                            WorkerThreadRequest<void, T...>& req) {
+  std::apply(req.work, req.params);
+  if (req.afterWork) {
+    if (auto async = thr.m_async.m_async.lock()) {
+      async->Send(std::move(req.afterWork));
+    }
+  } else {
+    thr.m_promises.SetValue(req.promiseId);
+  }
+}
+
+template <typename R, typename... T>
+void WorkerThreadThread<R, T...>::Main() {
+  std::vector<Request> requests;
+  while (m_active) {
+    std::unique_lock lock(m_mutex);
+    m_cond.wait(lock, [&] { return !m_active || !m_requests.empty(); });
+    if (!m_active) {
+      break;
+    }
+
+    // don't want to hold the lock while executing the callbacks
+    requests.swap(m_requests);
+    lock.unlock();
+
+    for (auto&& req : requests) {
+      if (!m_active) {
+        break;  // requests may be long-running
+      }
+      RunWorkerThreadRequest(*this, req);
+    }
+    requests.clear();
+    m_promises.Notify();
+  }
+}
+
+}  // namespace detail
+
+template <typename T>
+class WorkerThread;
+
+template <typename R, typename... T>
+class WorkerThread<R(T...)> final {
+  using Thread = detail::WorkerThreadThread<R, T...>;
+
+ public:
+  using WorkFunction = std::function<R(T...)>;
+  using AfterWorkFunction =
+      typename detail::WorkerThreadAsync<R>::AfterWorkFunction;
+
+  WorkerThread() { m_owner.Start(); }
+
+  /**
+   * Set the loop.  This must be called from the loop thread.
+   * Subsequent calls to QueueWorkThen will run afterWork on the provided
+   * loop (via an async handle).
+   *
+   * @param loop the loop to use for running afterWork routines
+   */
+  void SetLoop(uv::Loop& loop) {
+    if (auto thr = m_owner.GetThread()) {
+      thr->m_async.SetLoop(loop);
+    }
+  }
+
+  /**
+   * Set the loop.  This must be called from the loop thread.
+   * Subsequent calls to QueueWorkThen will run afterWork on the provided
+   * loop (via an async handle).
+   *
+   * @param loop the loop to use for running afterWork routines
+   */
+  void SetLoop(std::shared_ptr<uv::Loop> loop) { SetLoop(*loop); }
+
+  /**
+   * Unset the loop.  This must be called from the loop thread.
+   * Subsequent calls to QueueWorkThen will no longer run afterWork.
+   */
+  void UnsetLoop() {
+    if (auto thr = m_owner.GetThread()) {
+      thr->m_async.UnsetLoop();
+    }
+  }
+
+  /**
+   * Get the handle used by QueueWorkThen() to run afterWork.
+   * This handle is set by SetLoop().
+   * Calling Close() on this handle is the same as calling UnsetLoop().
+   *
+   * @return The handle (if nullptr, no handle is set)
+   */
+  std::shared_ptr<uv::Handle> GetHandle() const {
+    if (auto thr = m_owner.GetThread()) {
+      return thr->m_async.m_async.lock();
+    } else {
+      return nullptr;
+    }
+  }
+
+  /**
+   * Wakeup the worker thread, call the work function, and return a future for
+   * the result.
+   *
+   * It’s safe to call this function from any thread.
+   * The work function will be called on the worker thread.
+   *
+   * The future will return a default-constructed result if this class is
+   * destroyed while waiting for a result.
+   *
+   * @param work Work function (called on worker thread)
+   * @param u Arguments to work function
+   */
+  template <typename... U>
+  future<R> QueueWork(WorkFunction work, U&&... u) {
+    if (auto thr = m_owner.GetThread()) {
+      // create the future
+      uint64_t req = thr->m_promises.CreateRequest();
+
+      // add the parameters to the input queue
+      thr->m_requests.emplace_back(
+          req, std::move(work), std::forward_as_tuple(std::forward<U>(u)...));
+
+      // signal the thread
+      thr->m_cond.notify_one();
+
+      // return future
+      return thr->m_promises.CreateFuture(req);
+    }
+
+    // XXX: is this the right thing to do?
+    return future<R>();
+  }
+
+  /**
+   * Wakeup the worker thread, call the work function, and call the afterWork
+   * function with the result on the loop set by SetLoop().
+   *
+   * It’s safe to call this function from any thread.
+   * The work function will be called on the worker thread, and the afterWork
+   * function will be called on the loop thread.
+   *
+   * SetLoop() must be called prior to calling this function for afterWork to
+   * be called.
+   *
+   * @param work Work function (called on worker thread)
+   * @param afterWork After work function (called on loop thread)
+   * @param u Arguments to work function
+   */
+  template <typename... U>
+  void QueueWorkThen(WorkFunction work, AfterWorkFunction afterWork, U&&... u) {
+    if (auto thr = m_owner.GetThread()) {
+      // add the parameters to the input queue
+      thr->m_requests.emplace_back(
+          std::move(work), std::move(afterWork),
+          std::forward_as_tuple(std::forward<U>(u)...));
+
+      // signal the thread
+      thr->m_cond.notify_one();
+    }
+  }
+
+ private:
+  SafeThreadOwner<Thread> m_owner;
+};
+
+}  // namespace wpi
+
+#endif  // WPINET_WORKERTHREAD_H_
diff --git a/wpinet/src/main/native/include/wpinet/hostname.h b/wpinet/src/main/native/include/wpinet/hostname.h
new file mode 100644
index 0000000..e356fb4
--- /dev/null
+++ b/wpinet/src/main/native/include/wpinet/hostname.h
@@ -0,0 +1,19 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#ifndef WPINET_HOSTNAME_H_
+#define WPINET_HOSTNAME_H_
+
+#include <string>
+#include <string_view>
+
+namespace wpi {
+template <typename T>
+class SmallVectorImpl;
+
+std::string GetHostname();
+std::string_view GetHostname(SmallVectorImpl<char>& name);
+}  // namespace wpi
+
+#endif  // WPINET_HOSTNAME_H_
diff --git a/wpinet/src/main/native/include/wpinet/http_parser.h b/wpinet/src/main/native/include/wpinet/http_parser.h
new file mode 100644
index 0000000..2189b8f
--- /dev/null
+++ b/wpinet/src/main/native/include/wpinet/http_parser.h
@@ -0,0 +1,421 @@
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+#ifndef wpi_http_parser_h
+#define wpi_http_parser_h
+
+/* Also update SONAME in the Makefile whenever you change these. */
+#define HTTP_PARSER_VERSION_MAJOR 2
+#define HTTP_PARSER_VERSION_MINOR 8
+#define HTTP_PARSER_VERSION_PATCH 1
+
+#include <stddef.h>
+#include <stdint.h>
+
+/* Compile with -DHTTP_PARSER_STRICT=0 to make less checks, but run
+ * faster
+ */
+#ifndef HTTP_PARSER_STRICT
+# define HTTP_PARSER_STRICT 1
+#endif
+
+/* Maximium header size allowed. If the macro is not defined
+ * before including this header then the default is used. To
+ * change the maximum header size, define the macro in the build
+ * environment (e.g. -DHTTP_MAX_HEADER_SIZE=<value>). To remove
+ * the effective limit on the size of the header, define the macro
+ * to a very large number (e.g. -DHTTP_MAX_HEADER_SIZE=0x7fffffff)
+ */
+#ifndef HTTP_MAX_HEADER_SIZE
+# define HTTP_MAX_HEADER_SIZE (80*1024)
+#endif
+
+namespace wpi {
+
+struct http_parser;
+struct http_parser_settings;
+
+
+/* Callbacks should return non-zero to indicate an error. The parser will
+ * then halt execution.
+ *
+ * The one exception is on_headers_complete. In a HTTP_RESPONSE parser
+ * returning '1' from on_headers_complete will tell the parser that it
+ * should not expect a body. This is used when receiving a response to a
+ * HEAD request which may contain 'Content-Length' or 'Transfer-Encoding:
+ * chunked' headers that indicate the presence of a body.
+ *
+ * Returning `2` from on_headers_complete will tell parser that it should not
+ * expect neither a body nor any further responses on this connection. This is
+ * useful for handling responses to a CONNECT request which may not contain
+ * `Upgrade` or `Connection: upgrade` headers.
+ *
+ * http_data_cb does not return data chunks. It will be called arbitrarily
+ * many times for each string. E.G. you might get 10 callbacks for "on_url"
+ * each providing just a few characters more data.
+ */
+typedef int (*http_data_cb) (http_parser*, const char *at, size_t length);
+typedef int (*http_cb) (http_parser*);
+
+
+/* Status Codes */
+#define HTTP_STATUS_MAP(XX)                                                 \
+  XX(100, CONTINUE,                        Continue)                        \
+  XX(101, SWITCHING_PROTOCOLS,             Switching Protocols)             \
+  XX(102, PROCESSING,                      Processing)                      \
+  XX(200, OK,                              OK)                              \
+  XX(201, CREATED,                         Created)                         \
+  XX(202, ACCEPTED,                        Accepted)                        \
+  XX(203, NON_AUTHORITATIVE_INFORMATION,   Non-Authoritative Information)   \
+  XX(204, NO_CONTENT,                      No Content)                      \
+  XX(205, RESET_CONTENT,                   Reset Content)                   \
+  XX(206, PARTIAL_CONTENT,                 Partial Content)                 \
+  XX(207, MULTI_STATUS,                    Multi-Status)                    \
+  XX(208, ALREADY_REPORTED,                Already Reported)                \
+  XX(226, IM_USED,                         IM Used)                         \
+  XX(300, MULTIPLE_CHOICES,                Multiple Choices)                \
+  XX(301, MOVED_PERMANENTLY,               Moved Permanently)               \
+  XX(302, FOUND,                           Found)                           \
+  XX(303, SEE_OTHER,                       See Other)                       \
+  XX(304, NOT_MODIFIED,                    Not Modified)                    \
+  XX(305, USE_PROXY,                       Use Proxy)                       \
+  XX(307, TEMPORARY_REDIRECT,              Temporary Redirect)              \
+  XX(308, PERMANENT_REDIRECT,              Permanent Redirect)              \
+  XX(400, BAD_REQUEST,                     Bad Request)                     \
+  XX(401, UNAUTHORIZED,                    Unauthorized)                    \
+  XX(402, PAYMENT_REQUIRED,                Payment Required)                \
+  XX(403, FORBIDDEN,                       Forbidden)                       \
+  XX(404, NOT_FOUND,                       Not Found)                       \
+  XX(405, METHOD_NOT_ALLOWED,              Method Not Allowed)              \
+  XX(406, NOT_ACCEPTABLE,                  Not Acceptable)                  \
+  XX(407, PROXY_AUTHENTICATION_REQUIRED,   Proxy Authentication Required)   \
+  XX(408, REQUEST_TIMEOUT,                 Request Timeout)                 \
+  XX(409, CONFLICT,                        Conflict)                        \
+  XX(410, GONE,                            Gone)                            \
+  XX(411, LENGTH_REQUIRED,                 Length Required)                 \
+  XX(412, PRECONDITION_FAILED,             Precondition Failed)             \
+  XX(413, PAYLOAD_TOO_LARGE,               Payload Too Large)               \
+  XX(414, URI_TOO_LONG,                    URI Too Long)                    \
+  XX(415, UNSUPPORTED_MEDIA_TYPE,          Unsupported Media Type)          \
+  XX(416, RANGE_NOT_SATISFIABLE,           Range Not Satisfiable)           \
+  XX(417, EXPECTATION_FAILED,              Expectation Failed)              \
+  XX(421, MISDIRECTED_REQUEST,             Misdirected Request)             \
+  XX(422, UNPROCESSABLE_ENTITY,            Unprocessable Entity)            \
+  XX(423, LOCKED,                          Locked)                          \
+  XX(424, FAILED_DEPENDENCY,               Failed Dependency)               \
+  XX(426, UPGRADE_REQUIRED,                Upgrade Required)                \
+  XX(428, PRECONDITION_REQUIRED,           Precondition Required)           \
+  XX(429, TOO_MANY_REQUESTS,               Too Many Requests)               \
+  XX(431, REQUEST_HEADER_FIELDS_TOO_LARGE, Request Header Fields Too Large) \
+  XX(451, UNAVAILABLE_FOR_LEGAL_REASONS,   Unavailable For Legal Reasons)   \
+  XX(500, INTERNAL_SERVER_ERROR,           Internal Server Error)           \
+  XX(501, NOT_IMPLEMENTED,                 Not Implemented)                 \
+  XX(502, BAD_GATEWAY,                     Bad Gateway)                     \
+  XX(503, SERVICE_UNAVAILABLE,             Service Unavailable)             \
+  XX(504, GATEWAY_TIMEOUT,                 Gateway Timeout)                 \
+  XX(505, HTTP_VERSION_NOT_SUPPORTED,      HTTP Version Not Supported)      \
+  XX(506, VARIANT_ALSO_NEGOTIATES,         Variant Also Negotiates)         \
+  XX(507, INSUFFICIENT_STORAGE,            Insufficient Storage)            \
+  XX(508, LOOP_DETECTED,                   Loop Detected)                   \
+  XX(510, NOT_EXTENDED,                    Not Extended)                    \
+  XX(511, NETWORK_AUTHENTICATION_REQUIRED, Network Authentication Required) \
+
+enum http_status
+  {
+#define XX(num, name, string) HTTP_STATUS_##name = num,
+  HTTP_STATUS_MAP(XX)
+#undef XX
+  };
+
+
+/* Request Methods */
+#define HTTP_METHOD_MAP(XX)         \
+  XX(0,  DELETE,      DELETE)       \
+  XX(1,  GET,         GET)          \
+  XX(2,  HEAD,        HEAD)         \
+  XX(3,  POST,        POST)         \
+  XX(4,  PUT,         PUT)          \
+  /* pathological */                \
+  XX(5,  CONNECT,     CONNECT)      \
+  XX(6,  OPTIONS,     OPTIONS)      \
+  XX(7,  TRACE,       TRACE)        \
+  /* WebDAV */                      \
+  XX(8,  COPY,        COPY)         \
+  XX(9,  LOCK,        LOCK)         \
+  XX(10, MKCOL,       MKCOL)        \
+  XX(11, MOVE,        MOVE)         \
+  XX(12, PROPFIND,    PROPFIND)     \
+  XX(13, PROPPATCH,   PROPPATCH)    \
+  XX(14, SEARCH,      SEARCH)       \
+  XX(15, UNLOCK,      UNLOCK)       \
+  XX(16, BIND,        BIND)         \
+  XX(17, REBIND,      REBIND)       \
+  XX(18, UNBIND,      UNBIND)       \
+  XX(19, ACL,         ACL)          \
+  /* subversion */                  \
+  XX(20, REPORT,      REPORT)       \
+  XX(21, MKACTIVITY,  MKACTIVITY)   \
+  XX(22, CHECKOUT,    CHECKOUT)     \
+  XX(23, MERGE,       MERGE)        \
+  /* upnp */                        \
+  XX(24, MSEARCH,     M-SEARCH)     \
+  XX(25, NOTIFY,      NOTIFY)       \
+  XX(26, SUBSCRIBE,   SUBSCRIBE)    \
+  XX(27, UNSUBSCRIBE, UNSUBSCRIBE)  \
+  /* RFC-5789 */                    \
+  XX(28, PATCH,       PATCH)        \
+  XX(29, PURGE,       PURGE)        \
+  /* CalDAV */                      \
+  XX(30, MKCALENDAR,  MKCALENDAR)   \
+  /* RFC-2068, section 19.6.1.2 */  \
+  XX(31, LINK,        LINK)         \
+  XX(32, UNLINK,      UNLINK)       \
+  /* icecast */                     \
+  XX(33, SOURCE,      SOURCE)       \
+
+enum http_method
+  {
+#define XX(num, name, string) HTTP_##name = num,
+  HTTP_METHOD_MAP(XX)
+#undef XX
+  };
+
+
+enum http_parser_type { HTTP_REQUEST, HTTP_RESPONSE, HTTP_BOTH };
+
+
+/* Flag values for http_parser.flags field */
+enum flags
+  { F_CHUNKED               = 1 << 0
+  , F_CONNECTION_KEEP_ALIVE = 1 << 1
+  , F_CONNECTION_CLOSE      = 1 << 2
+  , F_CONNECTION_UPGRADE    = 1 << 3
+  , F_TRAILING              = 1 << 4
+  , F_UPGRADE               = 1 << 5
+  , F_SKIPBODY              = 1 << 6
+  , F_CONTENTLENGTH         = 1 << 7
+  };
+
+
+/* Map for errno-related constants
+ *
+ * The provided argument should be a macro that takes 2 arguments.
+ */
+#define HTTP_ERRNO_MAP(XX)                                           \
+  /* No error */                                                     \
+  XX(OK, "success")                                                  \
+                                                                     \
+  /* Callback-related errors */                                      \
+  XX(CB_message_begin, "the on_message_begin callback failed")       \
+  XX(CB_url, "the on_url callback failed")                           \
+  XX(CB_header_field, "the on_header_field callback failed")         \
+  XX(CB_header_value, "the on_header_value callback failed")         \
+  XX(CB_headers_complete, "the on_headers_complete callback failed") \
+  XX(CB_body, "the on_body callback failed")                         \
+  XX(CB_message_complete, "the on_message_complete callback failed") \
+  XX(CB_status, "the on_status callback failed")                     \
+  XX(CB_chunk_header, "the on_chunk_header callback failed")         \
+  XX(CB_chunk_complete, "the on_chunk_complete callback failed")     \
+                                                                     \
+  /* Parsing-related errors */                                       \
+  XX(INVALID_EOF_STATE, "stream ended at an unexpected time")        \
+  XX(HEADER_OVERFLOW,                                                \
+     "too many header bytes seen; overflow detected")                \
+  XX(CLOSED_CONNECTION,                                              \
+     "data received after completed connection: close message")      \
+  XX(INVALID_VERSION, "invalid HTTP version")                        \
+  XX(INVALID_STATUS, "invalid HTTP status code")                     \
+  XX(INVALID_METHOD, "invalid HTTP method")                          \
+  XX(INVALID_URL, "invalid URL")                                     \
+  XX(INVALID_HOST, "invalid host")                                   \
+  XX(INVALID_PORT, "invalid port")                                   \
+  XX(INVALID_PATH, "invalid path")                                   \
+  XX(INVALID_QUERY_STRING, "invalid query string")                   \
+  XX(INVALID_FRAGMENT, "invalid fragment")                           \
+  XX(LF_EXPECTED, "LF character expected")                           \
+  XX(INVALID_HEADER_TOKEN, "invalid character in header")            \
+  XX(INVALID_CONTENT_LENGTH,                                         \
+     "invalid character in content-length header")                   \
+  XX(UNEXPECTED_CONTENT_LENGTH,                                      \
+     "unexpected content-length header")                             \
+  XX(INVALID_CHUNK_SIZE,                                             \
+     "invalid character in chunk size header")                       \
+  XX(INVALID_CONSTANT, "invalid constant string")                    \
+  XX(INVALID_INTERNAL_STATE, "encountered unexpected internal state")\
+  XX(STRICT, "strict mode assertion failed")                         \
+  XX(PAUSED, "parser is paused")                                     \
+  XX(UNKNOWN, "an unknown error occurred")
+
+
+/* Define HPE_* values for each errno value above */
+#define HTTP_ERRNO_GEN(n, s) HPE_##n,
+enum http_errno {
+  HTTP_ERRNO_MAP(HTTP_ERRNO_GEN)
+};
+#undef HTTP_ERRNO_GEN
+
+
+/* Get an http_errno value from an http_parser */
+#define HTTP_PARSER_ERRNO(p)            ((::wpi::http_errno) (p)->http_errno)
+
+
+struct http_parser {
+  /** PRIVATE **/
+  unsigned int type : 2;         /* enum http_parser_type */
+  unsigned int flags : 8;        /* F_* values from 'flags' enum; semi-public */
+  unsigned int state : 7;        /* enum state from http_parser.c */
+  unsigned int header_state : 7; /* enum header_state from http_parser.c */
+  unsigned int index : 7;        /* index into current matcher */
+  unsigned int lenient_http_headers : 1;
+
+  uint32_t nread;          /* # bytes read in various scenarios */
+  uint64_t content_length; /* # bytes in body (0 if no Content-Length header) */
+
+  /** READ-ONLY **/
+  unsigned short http_major;
+  unsigned short http_minor;
+  unsigned int status_code : 16; /* responses only */
+  unsigned int method : 8;       /* requests only */
+  unsigned int http_errno : 7;
+
+  /* 1 = Upgrade header was present and the parser has exited because of that.
+   * 0 = No upgrade header present.
+   * Should be checked when http_parser_execute() returns in addition to
+   * error checking.
+   */
+  unsigned int upgrade : 1;
+
+  /** PUBLIC **/
+  void *data; /* A pointer to get hook to the "connection" or "socket" object */
+};
+
+
+struct http_parser_settings {
+  http_cb      on_message_begin;
+  http_data_cb on_url;
+  http_data_cb on_status;
+  http_data_cb on_header_field;
+  http_data_cb on_header_value;
+  http_cb      on_headers_complete;
+  http_data_cb on_body;
+  http_cb      on_message_complete;
+  /* When on_chunk_header is called, the current chunk length is stored
+   * in parser->content_length.
+   */
+  http_cb      on_chunk_header;
+  http_cb      on_chunk_complete;
+};
+
+
+enum http_parser_url_fields
+  { UF_SCHEMA           = 0
+  , UF_HOST             = 1
+  , UF_PORT             = 2
+  , UF_PATH             = 3
+  , UF_QUERY            = 4
+  , UF_FRAGMENT         = 5
+  , UF_USERINFO         = 6
+  , UF_MAX              = 7
+  };
+
+
+/* Result structure for http_parser_parse_url().
+ *
+ * Callers should index into field_data[] with UF_* values iff field_set
+ * has the relevant (1 << UF_*) bit set. As a courtesy to clients (and
+ * because we probably have padding left over), we convert any port to
+ * a uint16_t.
+ */
+struct http_parser_url {
+  uint16_t field_set;           /* Bitmask of (1 << UF_*) values */
+  uint16_t port;                /* Converted UF_PORT string */
+
+  struct {
+    uint16_t off;               /* Offset into buffer in which field starts */
+    uint16_t len;               /* Length of run in buffer */
+  } field_data[UF_MAX];
+};
+
+
+/* Returns the library version. Bits 16-23 contain the major version number,
+ * bits 8-15 the minor version number and bits 0-7 the patch level.
+ * Usage example:
+ *
+ *   unsigned long version = http_parser_version();
+ *   unsigned major = (version >> 16) & 255;
+ *   unsigned minor = (version >> 8) & 255;
+ *   unsigned patch = version & 255;
+ *   printf("http_parser v%u.%u.%u\n", major, minor, patch);
+ */
+unsigned long http_parser_version(void);
+
+void http_parser_init(http_parser *parser, enum http_parser_type type);
+
+
+/* Initialize http_parser_settings members to 0
+ */
+void http_parser_settings_init(http_parser_settings *settings);
+
+
+/* Executes the parser. Returns number of parsed bytes. Sets
+ * `parser->http_errno` on error. */
+size_t http_parser_execute(http_parser *parser,
+                           const http_parser_settings *settings,
+                           const char *data,
+                           size_t len);
+
+
+/* If http_should_keep_alive() in the on_headers_complete or
+ * on_message_complete callback returns 0, then this should be
+ * the last message on the connection.
+ * If you are the server, respond with the "Connection: close" header.
+ * If you are the client, close the connection.
+ */
+int http_should_keep_alive(const http_parser *parser);
+
+/* Returns a string version of the HTTP method. */
+const char *http_method_str(enum http_method m);
+
+/* Returns a string version of the HTTP status code. */
+const char *http_status_str(enum http_status s);
+
+/* Return a string name of the given error */
+const char *http_errno_name(enum http_errno err);
+
+/* Return a string description of the given error */
+const char *http_errno_description(enum http_errno err);
+
+/* Initialize all http_parser_url members to 0 */
+void http_parser_url_init(struct http_parser_url *u);
+
+/* Parse a URL; return nonzero on failure */
+int http_parser_parse_url(const char *buf, size_t buflen,
+                          int is_connect,
+                          struct http_parser_url *u);
+
+/* Pause or un-pause the parser; a nonzero value pauses */
+void http_parser_pause(http_parser *parser, int paused);
+
+/* Checks if this is the final chunk of the body. */
+int http_body_is_final(const http_parser *parser);
+
+}  // namespace wpi
+
+#endif
diff --git a/wpinet/src/main/native/include/wpinet/raw_socket_istream.h b/wpinet/src/main/native/include/wpinet/raw_socket_istream.h
new file mode 100644
index 0000000..f8f41e3
--- /dev/null
+++ b/wpinet/src/main/native/include/wpinet/raw_socket_istream.h
@@ -0,0 +1,31 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#ifndef WPINET_RAW_SOCKET_ISTREAM_H_
+#define WPINET_RAW_SOCKET_ISTREAM_H_
+
+#include <wpi/raw_istream.h>
+
+namespace wpi {
+
+class NetworkStream;
+
+class raw_socket_istream : public raw_istream {
+ public:
+  explicit raw_socket_istream(NetworkStream& stream, int timeout = 0)
+      : m_stream(stream), m_timeout(timeout) {}
+
+  void close() override;
+  size_t in_avail() const override;
+
+ private:
+  void read_impl(void* data, size_t len) override;
+
+  NetworkStream& m_stream;
+  int m_timeout;
+};
+
+}  // namespace wpi
+
+#endif  // WPINET_RAW_SOCKET_ISTREAM_H_
diff --git a/wpinet/src/main/native/include/wpinet/raw_socket_ostream.h b/wpinet/src/main/native/include/wpinet/raw_socket_ostream.h
new file mode 100644
index 0000000..51e0104
--- /dev/null
+++ b/wpinet/src/main/native/include/wpinet/raw_socket_ostream.h
@@ -0,0 +1,39 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#ifndef WPINET_RAW_SOCKET_OSTREAM_H_
+#define WPINET_RAW_SOCKET_OSTREAM_H_
+
+#include <wpi/raw_ostream.h>
+
+namespace wpi {
+
+class NetworkStream;
+
+class raw_socket_ostream : public raw_ostream {
+ public:
+  raw_socket_ostream(NetworkStream& stream, bool shouldClose)
+      : m_stream(stream), m_shouldClose(shouldClose) {}
+  ~raw_socket_ostream() override;
+
+  void close();
+
+  bool has_error() const { return m_error; }
+  void clear_error() { m_error = false; }
+
+ protected:
+  void error_detected() { m_error = true; }
+
+ private:
+  void write_impl(const char* data, size_t len) override;
+  uint64_t current_pos() const override;
+
+  NetworkStream& m_stream;
+  bool m_error = false;
+  bool m_shouldClose;
+};
+
+}  // namespace wpi
+
+#endif  // WPINET_RAW_SOCKET_OSTREAM_H_
diff --git a/wpinet/src/main/native/include/wpinet/raw_uv_ostream.h b/wpinet/src/main/native/include/wpinet/raw_uv_ostream.h
new file mode 100644
index 0000000..aa45297
--- /dev/null
+++ b/wpinet/src/main/native/include/wpinet/raw_uv_ostream.h
@@ -0,0 +1,75 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#ifndef WPINET_RAW_UV_OSTREAM_H_
+#define WPINET_RAW_UV_OSTREAM_H_
+
+#include <functional>
+#include <span>
+#include <utility>
+
+#include <wpi/SmallVector.h>
+#include <wpi/raw_ostream.h>
+
+#include "wpinet/uv/Buffer.h"
+
+namespace wpi {
+
+/**
+ * raw_ostream style output to a SmallVector of uv::Buffer buffers.  Fixed-size
+ * buffers are allocated and appended as necessary to fit the data being output.
+ * The SmallVector need not be empty at start.
+ */
+class raw_uv_ostream : public raw_ostream {
+ public:
+  /**
+   * Construct a new raw_uv_ostream.
+   * @param bufs Buffers vector.  NOT cleared on construction.
+   * @param allocSize Size to allocate for each buffer; allocation will be
+   *                  performed using Buffer::Allocate().
+   */
+  raw_uv_ostream(SmallVectorImpl<uv::Buffer>& bufs, size_t allocSize)
+      : m_bufs(bufs), m_alloc([=] { return uv::Buffer::Allocate(allocSize); }) {
+    SetUnbuffered();
+  }
+
+  /**
+   * Construct a new raw_uv_ostream.
+   * @param bufs Buffers vector.  NOT cleared on construction.
+   * @param alloc Allocator.
+   */
+  raw_uv_ostream(SmallVectorImpl<uv::Buffer>& bufs,
+                 std::function<uv::Buffer()> alloc)
+      : m_bufs(bufs), m_alloc(std::move(alloc)) {
+    SetUnbuffered();
+  }
+
+  ~raw_uv_ostream() override = default;
+
+  /**
+   * Returns an span to the buffers.
+   */
+  std::span<uv::Buffer> bufs() { return m_bufs; }
+
+  void flush() = delete;
+
+  /**
+   * Resets the amount of allocated space.
+   */
+  void reset() { m_left = 0; }
+
+ private:
+  void write_impl(const char* data, size_t len) override;
+  uint64_t current_pos() const override;
+
+  SmallVectorImpl<uv::Buffer>& m_bufs;
+  std::function<uv::Buffer()> m_alloc;
+
+  // How much allocated space is left in the current buffer.
+  size_t m_left = 0;
+};
+
+}  // namespace wpi
+
+#endif  // WPINET_RAW_UV_OSTREAM_H_
diff --git a/wpinet/src/main/native/include/wpinet/uv/Async.h b/wpinet/src/main/native/include/wpinet/uv/Async.h
new file mode 100644
index 0000000..eb3a005
--- /dev/null
+++ b/wpinet/src/main/native/include/wpinet/uv/Async.h
@@ -0,0 +1,193 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#ifndef WPINET_UV_ASYNC_H_
+#define WPINET_UV_ASYNC_H_
+
+#include <uv.h>
+
+#include <memory>
+#include <thread>
+#include <tuple>
+#include <utility>
+#include <vector>
+
+#include <wpi/Signal.h>
+#include <wpi/mutex.h>
+
+#include "wpinet/uv/Handle.h"
+#include "wpinet/uv/Loop.h"
+
+namespace wpi::uv {
+
+/**
+ * Async handle.
+ * Async handles allow the user to "wakeup" the event loop and have a signal
+ * generated from another thread.
+ *
+ * Data may be passed into the callback called on the event loop by using
+ * template parameters.  If data parameters are used, the async callback will
+ * be called once for every call to Send().  If no data parameters are used,
+ * the async callback may or may not be called for every call to Send() (e.g.
+ * the calls may be coaleasced).
+ */
+template <typename... T>
+class Async final : public HandleImpl<Async<T...>, uv_async_t> {
+  struct private_init {};
+
+ public:
+  Async(const std::shared_ptr<Loop>& loop, const private_init&)
+      : m_loop{loop} {}
+  ~Async() noexcept override {
+    if (auto loop = m_loop.lock()) {
+      this->Close();
+    } else {
+      this->ForceClosed();
+    }
+  }
+
+  /**
+   * Create an async handle.
+   *
+   * @param loop Loop object where this handle runs.
+   */
+  static std::shared_ptr<Async> Create(Loop& loop) {
+    return Create(loop.shared_from_this());
+  }
+
+  /**
+   * Create an async handle.
+   *
+   * @param loop Loop object where this handle runs.
+   */
+  static std::shared_ptr<Async> Create(const std::shared_ptr<Loop>& loop) {
+    auto h = std::make_shared<Async>(loop, private_init{});
+    int err =
+        uv_async_init(loop->GetRaw(), h->GetRaw(), [](uv_async_t* handle) {
+          auto& h = *static_cast<Async*>(handle->data);
+          std::scoped_lock lock(h.m_mutex);
+          for (auto&& v : h.m_data) {
+            std::apply(h.wakeup, v);
+          }
+          h.m_data.clear();
+        });
+    if (err < 0) {
+      loop->ReportError(err);
+      return nullptr;
+    }
+    h->Keep();
+    return h;
+  }
+
+  /**
+   * Wakeup the event loop and emit the event.
+   *
+   * It’s safe to call this function from any thread including the loop thread.
+   * An async event will be emitted on the loop thread.
+   */
+  template <typename... U>
+  void Send(U&&... u) {
+    auto loop = m_loop.lock();
+    if (loop && loop->GetThreadId() == std::this_thread::get_id()) {
+      // called from within the loop, just call the function directly
+      wakeup(std::forward<U>(u)...);
+      return;
+    }
+
+    {
+      std::scoped_lock lock(m_mutex);
+      m_data.emplace_back(std::forward_as_tuple(std::forward<U>(u)...));
+    }
+    if (loop) {
+      this->Invoke(&uv_async_send, this->GetRaw());
+    }
+  }
+
+  /**
+   * Wakeup the event loop and emit the event.
+   * This function assumes the loop still exists, which makes it a bit faster.
+   *
+   * It’s safe to call this function from any thread.
+   * An async event will be emitted on the loop thread.
+   */
+  void UnsafeSend() { Invoke(&uv_async_send, this->GetRaw()); }
+
+  /**
+   * Signal generated (on event loop thread) when the async event occurs.
+   */
+  sig::Signal<T...> wakeup;
+
+ private:
+  wpi::mutex m_mutex;
+  std::vector<std::tuple<T...>> m_data;
+  std::weak_ptr<Loop> m_loop;
+};
+
+/**
+ * Async specialization for no data parameters.  The async callback may or may
+ * not be called for every call to Send() (e.g. the calls may be coaleasced).
+ */
+template <>
+class Async<> final : public HandleImpl<Async<>, uv_async_t> {
+  struct private_init {};
+
+ public:
+  Async(const std::shared_ptr<Loop>& loop, const private_init&)
+      : m_loop(loop) {}
+  ~Async() noexcept override;
+
+  /**
+   * Create an async handle.
+   *
+   * @param loop Loop object where this handle runs.
+   */
+  static std::shared_ptr<Async> Create(Loop& loop) {
+    return Create(loop.shared_from_this());
+  }
+
+  /**
+   * Create an async handle.
+   *
+   * @param loop Loop object where this handle runs.
+   */
+  static std::shared_ptr<Async> Create(const std::shared_ptr<Loop>& loop);
+
+  /**
+   * Wakeup the event loop and emit the event.
+   *
+   * It’s safe to call this function from any thread.
+   * An async event will be emitted on the loop thread.
+   */
+  void Send() {
+    if (auto loop = m_loop.lock()) {
+      if (loop->GetThreadId() == std::this_thread::get_id()) {
+        // called from within the loop, just call the function directly
+        wakeup();
+      } else {
+        Invoke(&uv_async_send, GetRaw());
+      }
+    }
+  }
+
+  /**
+   * Wakeup the event loop and emit the event.
+   * This function assumes the loop still exists, which makes it a bit faster.
+   *
+   * It’s safe to call this function from any thread.
+   * An async event will be emitted on the loop thread.
+   */
+  void UnsafeSend() { Invoke(&uv_async_send, GetRaw()); }
+
+  /**
+   * Signal generated (on event loop thread) when the async event occurs.
+   */
+  sig::Signal<> wakeup;
+
+ private:
+  std::weak_ptr<Loop> m_loop;
+};
+
+}  // namespace wpi::uv
+
+#endif  // WPINET_UV_ASYNC_H_
diff --git a/wpinet/src/main/native/include/wpinet/uv/AsyncFunction.h b/wpinet/src/main/native/include/wpinet/uv/AsyncFunction.h
new file mode 100644
index 0000000..82a5913
--- /dev/null
+++ b/wpinet/src/main/native/include/wpinet/uv/AsyncFunction.h
@@ -0,0 +1,168 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#ifndef WPINET_UV_ASYNCFUNCTION_H_
+#define WPINET_UV_ASYNCFUNCTION_H_
+
+#include <stdint.h>
+#include <uv.h>
+
+#include <functional>
+#include <memory>
+#include <thread>
+#include <tuple>
+#include <utility>
+#include <vector>
+
+#include <wpi/future.h>
+#include <wpi/mutex.h>
+
+#include "wpinet/uv/Handle.h"
+#include "wpinet/uv/Loop.h"
+
+namespace wpi::uv {
+
+template <typename T>
+class AsyncFunction;
+
+/**
+ * Function async handle.
+ * Async handles allow the user to "wakeup" the event loop and have a function
+ * called from another thread that returns a result to the calling thread.
+ */
+template <typename R, typename... T>
+class AsyncFunction<R(T...)> final
+    : public HandleImpl<AsyncFunction<R(T...)>, uv_async_t> {
+  struct private_init {};
+
+ public:
+  AsyncFunction(const std::shared_ptr<Loop>& loop,
+                std::function<void(promise<R>, T...)> func, const private_init&)
+      : wakeup{std::move(func)}, m_loop{loop} {}
+  ~AsyncFunction() noexcept override {
+    if (auto loop = m_loop.lock()) {
+      this->Close();
+    } else {
+      this->ForceClosed();
+    }
+  }
+
+  /**
+   * Create an async handle.
+   *
+   * @param loop Loop object where this handle runs.
+   * @param func wakeup function to be called (sets wakeup value); the function
+   *             needs to return void, and its first parameter is the promise
+   *             for the result.  If no value is set on the promise by the
+   *             wakeup function, a default-constructed value is "returned".
+   */
+  static std::shared_ptr<AsyncFunction> Create(
+      Loop& loop, std::function<void(promise<R>, T...)> func = nullptr) {
+    return Create(loop.shared_from_this(), std::move(func));
+  }
+
+  /**
+   * Create an async handle.
+   *
+   * @param loop Loop object where this handle runs.
+   * @param func wakeup function to be called (sets wakeup value); the function
+   *             needs to return void, and its first parameter is the promise
+   *             for the result.  If no value is set on the promise by the
+   *             wakeup function, a default-constructed value is "returned".
+   */
+  static std::shared_ptr<AsyncFunction> Create(
+      const std::shared_ptr<Loop>& loop,
+      std::function<void(promise<R>, T...)> func = nullptr) {
+    auto h =
+        std::make_shared<AsyncFunction>(loop, std::move(func), private_init{});
+    int err =
+        uv_async_init(loop->GetRaw(), h->GetRaw(), [](uv_async_t* handle) {
+          auto& h = *static_cast<AsyncFunction*>(handle->data);
+          std::unique_lock lock(h.m_mutex);
+
+          if (!h.m_params.empty()) {
+            // for each set of parameters in the input queue, call the wakeup
+            // function and put the result in the output queue if the caller is
+            // waiting for it
+            for (auto&& v : h.m_params) {
+              auto p = h.m_promises.CreatePromise(v.first);
+              if (h.wakeup) {
+                std::apply(h.wakeup,
+                           std::tuple_cat(std::make_tuple(std::move(p)),
+                                          std::move(v.second)));
+              }
+            }
+            h.m_params.clear();
+            // wake up any threads that might be waiting for the result
+            lock.unlock();
+            h.m_promises.Notify();
+          }
+        });
+    if (err < 0) {
+      loop->ReportError(err);
+      return nullptr;
+    }
+    h->Keep();
+    return h;
+  }
+
+  /**
+   * Wakeup the event loop, call the async function, and return a future for
+   * the result.
+   *
+   * It’s safe to call this function from any thread including the loop thread.
+   * The async function will be called on the loop thread.
+   *
+   * The future will return a default-constructed result if this handle is
+   * destroyed while waiting for a result.
+   */
+  template <typename... U>
+  future<R> Call(U&&... u) {
+    // create the future
+    uint64_t req = m_promises.CreateRequest();
+
+    auto loop = m_loop.lock();
+    if (loop && loop->GetThreadId() == std::this_thread::get_id()) {
+      // called from within the loop, just call the function directly
+      wakeup(m_promises.CreatePromise(req), std::forward<U>(u)...);
+      return m_promises.CreateFuture(req);
+    }
+
+    // add the parameters to the input queue
+    {
+      std::scoped_lock lock(m_mutex);
+      m_params.emplace_back(std::piecewise_construct,
+                            std::forward_as_tuple(req),
+                            std::forward_as_tuple(std::forward<U>(u)...));
+    }
+
+    // signal the loop
+    if (loop) {
+      this->Invoke(&uv_async_send, this->GetRaw());
+    }
+
+    // return future
+    return m_promises.CreateFuture(req);
+  }
+
+  template <typename... U>
+  future<R> operator()(U&&... u) {
+    return Call(std::forward<U>(u)...);
+  }
+
+  /**
+   * Function called (on event loop thread) when the async is called.
+   */
+  std::function<void(promise<R>, T...)> wakeup;
+
+ private:
+  wpi::mutex m_mutex;
+  std::vector<std::pair<uint64_t, std::tuple<T...>>> m_params;
+  PromiseFactory<R> m_promises;
+  std::weak_ptr<Loop> m_loop;
+};
+
+}  // namespace wpi::uv
+
+#endif  // WPINET_UV_ASYNCFUNCTION_H_
diff --git a/wpinet/src/main/native/include/wpinet/uv/Buffer.h b/wpinet/src/main/native/include/wpinet/uv/Buffer.h
new file mode 100644
index 0000000..4b58b0f
--- /dev/null
+++ b/wpinet/src/main/native/include/wpinet/uv/Buffer.h
@@ -0,0 +1,163 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#ifndef WPINET_UV_BUFFER_H_
+#define WPINET_UV_BUFFER_H_
+
+#include <uv.h>
+
+#include <cstring>
+#include <initializer_list>
+#include <span>
+#include <string_view>
+#include <utility>
+
+#include <wpi/SmallVector.h>
+
+namespace wpi::uv {
+
+/**
+ * Data buffer.  Convenience wrapper around uv_buf_t.
+ */
+class Buffer : public uv_buf_t {
+ public:
+  Buffer() {
+    base = nullptr;
+    len = 0;
+  }
+  /*implicit*/ Buffer(const uv_buf_t& oth) {  // NOLINT
+    base = oth.base;
+    len = oth.len;
+  }
+  /*implicit*/ Buffer(std::string_view str)  // NOLINT
+      : Buffer{str.data(), str.size()} {}
+  /*implicit*/ Buffer(std::span<const uint8_t> arr)  // NOLINT
+      : Buffer{reinterpret_cast<const char*>(arr.data()), arr.size()} {}
+  Buffer(char* base_, size_t len_) {
+    base = base_;
+    len = static_cast<decltype(len)>(len_);
+  }
+  Buffer(const char* base_, size_t len_) {
+    base = const_cast<char*>(base_);
+    len = static_cast<decltype(len)>(len_);
+  }
+
+  std::span<const char> data() const { return {base, len}; }
+  std::span<char> data() { return {base, len}; }
+
+  operator std::span<const char>() const { return data(); }  // NOLINT
+  operator std::span<char>() { return data(); }              // NOLINT
+
+  static Buffer Allocate(size_t size) { return Buffer{new char[size], size}; }
+
+  static Buffer Dup(std::string_view in) {
+    Buffer buf = Allocate(in.size());
+    std::memcpy(buf.base, in.data(), in.size());
+    return buf;
+  }
+
+  static Buffer Dup(std::span<const uint8_t> in) {
+    Buffer buf = Allocate(in.size());
+    std::memcpy(buf.base, in.data(), in.size());
+    return buf;
+  }
+
+  Buffer Dup() const {
+    Buffer buf = Allocate(len);
+    std::memcpy(buf.base, base, len);
+    return buf;
+  }
+
+  void Deallocate() {
+    delete[] base;
+    base = nullptr;
+    len = 0;
+  }
+
+  Buffer Move() {
+    Buffer buf = *this;
+    base = nullptr;
+    len = 0;
+    return buf;
+  }
+
+  friend void swap(Buffer& a, Buffer& b) {
+    using std::swap;
+    swap(a.base, b.base);
+    swap(a.len, b.len);
+  }
+};
+
+/**
+ * A simple pool allocator for Buffers.
+ * Buffers are allocated individually but are reused rather than returned
+ * to the heap.
+ * @tparam DEPTH depth of pool
+ */
+template <size_t DEPTH = 4>
+class SimpleBufferPool {
+ public:
+  /**
+   * Constructor.
+   * @param size Size of each buffer to allocate.
+   */
+  explicit SimpleBufferPool(size_t size = 4096) : m_size{size} {}
+  ~SimpleBufferPool() { Clear(); }
+
+  SimpleBufferPool(const SimpleBufferPool& other) = delete;
+  SimpleBufferPool& operator=(const SimpleBufferPool& other) = delete;
+
+  /**
+   * Allocate a buffer.
+   */
+  Buffer Allocate() {
+    if (m_pool.empty()) {
+      return Buffer::Allocate(m_size);
+    }
+    auto buf = m_pool.back();
+    m_pool.pop_back();
+    buf.len = m_size;
+    return buf;
+  }
+
+  /**
+   * Allocate a buffer.
+   */
+  Buffer operator()() { return Allocate(); }
+
+  /**
+   * Release allocated buffers back into the pool.
+   * This is NOT safe to use with arbitrary buffers unless they were
+   * allocated with the same size as the buffer pool allocation size.
+   */
+  void Release(std::span<Buffer> bufs) {
+    for (auto& buf : bufs) {
+      m_pool.emplace_back(buf.Move());
+    }
+  }
+
+  /**
+   * Clear the pool, releasing all buffers.
+   */
+  void Clear() {
+    for (auto& buf : m_pool) {
+      buf.Deallocate();
+    }
+    m_pool.clear();
+  }
+
+  /**
+   * Get number of buffers left in the pool before a new buffer will be
+   * allocated from the heap.
+   */
+  size_t Remaining() const { return m_pool.size(); }
+
+ private:
+  SmallVector<Buffer, DEPTH> m_pool;
+  size_t m_size;  // NOLINT
+};
+
+}  // namespace wpi::uv
+
+#endif  // WPINET_UV_BUFFER_H_
diff --git a/wpinet/src/main/native/include/wpinet/uv/Check.h b/wpinet/src/main/native/include/wpinet/uv/Check.h
new file mode 100644
index 0000000..8f5aa2f
--- /dev/null
+++ b/wpinet/src/main/native/include/wpinet/uv/Check.h
@@ -0,0 +1,66 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#ifndef WPINET_UV_CHECK_H_
+#define WPINET_UV_CHECK_H_
+
+#include <uv.h>
+
+#include <memory>
+
+#include <wpi/Signal.h>
+
+#include "wpinet/uv/Handle.h"
+
+namespace wpi::uv {
+
+class Loop;
+
+/**
+ * Check handle.
+ * Check handles will generate a signal once per loop iteration, right
+ * after polling for I/O.
+ */
+class Check final : public HandleImpl<Check, uv_check_t> {
+  struct private_init {};
+
+ public:
+  explicit Check(const private_init&) {}
+  ~Check() noexcept override = default;
+
+  /**
+   * Create a check handle.
+   *
+   * @param loop Loop object where this handle runs.
+   */
+  static std::shared_ptr<Check> Create(Loop& loop);
+
+  /**
+   * Create a check handle.
+   *
+   * @param loop Loop object where this handle runs.
+   */
+  static std::shared_ptr<Check> Create(const std::shared_ptr<Loop>& loop) {
+    return Create(*loop);
+  }
+
+  /**
+   * Start the handle.
+   */
+  void Start();
+
+  /**
+   * Stop the handle.  The signal will no longer be generated.
+   */
+  void Stop() { Invoke(&uv_check_stop, GetRaw()); }
+
+  /**
+   * Signal generated once per loop iteration after polling for I/O.
+   */
+  sig::Signal<> check;
+};
+
+}  // namespace wpi::uv
+
+#endif  // WPINET_UV_CHECK_H_
diff --git a/wpinet/src/main/native/include/wpinet/uv/Error.h b/wpinet/src/main/native/include/wpinet/uv/Error.h
new file mode 100644
index 0000000..cc2a5d5
--- /dev/null
+++ b/wpinet/src/main/native/include/wpinet/uv/Error.h
@@ -0,0 +1,46 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#ifndef WPINET_UV_ERROR_H_
+#define WPINET_UV_ERROR_H_
+
+#include <uv.h>
+
+namespace wpi::uv {
+
+/**
+ * Error code.
+ */
+class Error {
+ public:
+  Error() = default;
+  explicit Error(int err) : m_err(err) {}
+
+  /**
+   * Boolean conversion.  Returns true if error, false if ok.
+   */
+  explicit operator bool() const { return m_err < 0; }
+
+  /**
+   * Returns the error code.
+   */
+  int code() const { return m_err; }
+
+  /**
+   * Returns the error message.
+   */
+  const char* str() const { return uv_strerror(m_err); }
+
+  /**
+   * Returns the error name.
+   */
+  const char* name() const { return uv_err_name(m_err); }
+
+ private:
+  int m_err{UV_UNKNOWN};
+};
+
+}  // namespace wpi::uv
+
+#endif  // WPINET_UV_ERROR_H_
diff --git a/wpinet/src/main/native/include/wpinet/uv/FsEvent.h b/wpinet/src/main/native/include/wpinet/uv/FsEvent.h
new file mode 100644
index 0000000..ba8a649
--- /dev/null
+++ b/wpinet/src/main/native/include/wpinet/uv/FsEvent.h
@@ -0,0 +1,80 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#ifndef WPINET_UV_FSEVENT_H_
+#define WPINET_UV_FSEVENT_H_
+
+#include <uv.h>
+
+#include <memory>
+#include <string>
+#include <string_view>
+
+#include <wpi/Signal.h>
+
+#include "wpinet/uv/Handle.h"
+
+namespace wpi::uv {
+
+class Loop;
+
+/**
+ * Filesystem Event handle.
+ */
+class FsEvent final : public HandleImpl<FsEvent, uv_fs_event_t> {
+  struct private_init {};
+
+ public:
+  explicit FsEvent(const private_init&) {}
+  ~FsEvent() noexcept override = default;
+
+  /**
+   * Create a filesystem event handle.
+   *
+   * @param loop Loop object where this handle runs.
+   */
+  static std::shared_ptr<FsEvent> Create(Loop& loop);
+
+  /**
+   * Create a filesystem event handle.
+   *
+   * @param loop Loop object where this handle runs.
+   */
+  static std::shared_ptr<FsEvent> Create(const std::shared_ptr<Loop>& loop) {
+    return Create(*loop);
+  }
+
+  /**
+   * Start watching the specified path for changes.
+   *
+   * @param path Path to watch for changes
+   * @param events Bitmask of event flags.  Only UV_FS_EVENT_RECURSIVE is
+   *               supported (and only on OSX and Windows).
+   */
+  void Start(std::string_view path, unsigned int flags = 0);
+
+  /**
+   * Stop watching for changes.
+   */
+  void Stop() { Invoke(&uv_fs_event_stop, GetRaw()); }
+
+  /**
+   * Get the path being monitored.  Signals error and returns empty string if
+   * an error occurs.
+   * @return Monitored path.
+   */
+  std::string GetPath();
+
+  /**
+   * Signal generated when a filesystem change occurs.  The first parameter
+   * is the filename (if a directory was passed to Start(), the filename is
+   * relative to that directory).  The second parameter is an ORed mask of
+   * UV_RENAME and UV_CHANGE.
+   */
+  sig::Signal<const char*, int> fsEvent;
+};
+
+}  // namespace wpi::uv
+
+#endif  // WPINET_UV_FSEVENT_H_
diff --git a/wpinet/src/main/native/include/wpinet/uv/GetAddrInfo.h b/wpinet/src/main/native/include/wpinet/uv/GetAddrInfo.h
new file mode 100644
index 0000000..5954110
--- /dev/null
+++ b/wpinet/src/main/native/include/wpinet/uv/GetAddrInfo.h
@@ -0,0 +1,120 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#ifndef WPINET_UV_GETADDRINFO_H_
+#define WPINET_UV_GETADDRINFO_H_
+
+#include <uv.h>
+
+#include <functional>
+#include <memory>
+#include <string_view>
+#include <utility>
+
+#include <wpi/Signal.h>
+
+#include "wpinet/uv/Request.h"
+
+namespace wpi::uv {
+
+class Loop;
+
+/**
+ * GetAddrInfo request.
+ * For use with `GetAddrInfo()` function family.
+ */
+class GetAddrInfoReq : public RequestImpl<GetAddrInfoReq, uv_getaddrinfo_t> {
+ public:
+  GetAddrInfoReq();
+
+  Loop& GetLoop() const { return *static_cast<Loop*>(GetRaw()->loop->data); }
+
+  /**
+   * Resolved lookup signal.
+   * Parameter is resolved address info.
+   */
+  sig::Signal<const addrinfo&> resolved;
+};
+
+/**
+ * Asynchronous getaddrinfo(3).  HandleResolvedAddress() is called on the
+ * request when the resolution completes.  HandleError() is called on the
+ * request if any errors occur.
+ *
+ * Either node or service may be empty but not both.
+ *
+ * @param loop Event loop
+ * @param req request
+ * @param node Either a numerical network address or a network hostname.
+ * @param service Either a service name or a port number as a string.
+ * @param hints Optional `addrinfo` data structure with additional address
+ *              type constraints.
+ */
+void GetAddrInfo(Loop& loop, const std::shared_ptr<GetAddrInfoReq>& req,
+                 std::string_view node, std::string_view service = {},
+                 const addrinfo* hints = nullptr);
+
+/**
+ * Asynchronous getaddrinfo(3).  HandleResolvedAddress() is called on the
+ * request when the resolution completes.  HandleError() is called on the
+ * request if any errors occur.
+ *
+ * Either node or service may be empty but not both.
+ *
+ * @param loop Event loop
+ * @param req request
+ * @param node Either a numerical network address or a network hostname.
+ * @param service Either a service name or a port number as a string.
+ * @param hints Optional `addrinfo` data structure with additional address
+ *              type constraints.
+ */
+inline void GetAddrInfo(const std::shared_ptr<Loop>& loop,
+                        const std::shared_ptr<GetAddrInfoReq>& req,
+                        std::string_view node, std::string_view service = {},
+                        const addrinfo* hints = nullptr) {
+  GetAddrInfo(*loop, req, node, service, hints);
+}
+
+/**
+ * Asynchronous getaddrinfo(3).  The callback is called when the resolution
+ * completes, and errors are forwarded to the loop.  This is a convenience
+ * wrapper.
+ *
+ * Either node or service may be empty but not both.
+ *
+ * @param loop Event loop
+ * @param callback Callback function to call when resolution completes
+ * @param node Either a numerical network address or a network hostname.
+ * @param service Either a service name or a port number as a string.
+ * @param hints Optional `addrinfo` data structure with additional address
+ *              type constraints.
+ */
+void GetAddrInfo(Loop& loop, std::function<void(const addrinfo&)> callback,
+                 std::string_view node, std::string_view service = {},
+                 const addrinfo* hints = nullptr);
+
+/**
+ * Asynchronous getaddrinfo(3).  The callback is called when the resolution
+ * completes, and errors are forwarded to the loop.  This is a convenience
+ * wrapper.
+ *
+ * Either node or service may be empty but not both.
+ *
+ * @param loop Event loop
+ * @param callback Callback function to call when resolution completes
+ * @param node Either a numerical network address or a network hostname.
+ * @param service Either a service name or a port number as a string.
+ * @param hints Optional `addrinfo` data structure with additional address
+ *              type constraints.
+ */
+inline void GetAddrInfo(const std::shared_ptr<Loop>& loop,
+                        std::function<void(const addrinfo&)> callback,
+                        std::string_view node, std::string_view service = {},
+                        const addrinfo* hints = nullptr) {
+  GetAddrInfo(*loop, std::move(callback), node, service, hints);
+}
+
+}  // namespace wpi::uv
+
+#endif  // WPINET_UV_GETADDRINFO_H_
diff --git a/wpinet/src/main/native/include/wpinet/uv/GetNameInfo.h b/wpinet/src/main/native/include/wpinet/uv/GetNameInfo.h
new file mode 100644
index 0000000..1bd0f4a
--- /dev/null
+++ b/wpinet/src/main/native/include/wpinet/uv/GetNameInfo.h
@@ -0,0 +1,229 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#ifndef WPINET_UV_GETNAMEINFO_H_
+#define WPINET_UV_GETNAMEINFO_H_
+
+#include <uv.h>
+
+#include <functional>
+#include <memory>
+#include <string_view>
+#include <utility>
+
+#include <wpi/Signal.h>
+
+#include "wpinet/uv/Request.h"
+
+namespace wpi::uv {
+
+class Loop;
+
+/**
+ * GetNameInfo request.
+ * For use with `GetNameInfo()` function family.
+ */
+class GetNameInfoReq : public RequestImpl<GetNameInfoReq, uv_getnameinfo_t> {
+ public:
+  GetNameInfoReq();
+
+  Loop& GetLoop() const { return *static_cast<Loop*>(GetRaw()->loop->data); }
+
+  /**
+   * Resolved lookup signal.
+   * Parameters are hostname and service.
+   */
+  sig::Signal<const char*, const char*> resolved;
+};
+
+/**
+ * Asynchronous getnameinfo(3).  HandleResolvedName() is called on the
+ * request when the resolution completes.  HandleError() is called on the
+ * request if any errors occur.
+ *
+ * @param loop Event loop
+ * @param req request
+ * @param addr Initialized `sockaddr_in` or `sockaddr_in6` data structure.
+ * @param flags Optional flags to modify the behavior of `getnameinfo`.
+ */
+void GetNameInfo(Loop& loop, const std::shared_ptr<GetNameInfoReq>& req,
+                 const sockaddr& addr, int flags = 0);
+
+/**
+ * Asynchronous getnameinfo(3).  HandleResolvedName() is called on the
+ * request when the resolution completes.  HandleError() is called on the
+ * request if any errors occur.
+ *
+ * @param loop Event loop
+ * @param req request
+ * @param addr Initialized `sockaddr_in` or `sockaddr_in6` data structure.
+ * @param flags Optional flags to modify the behavior of `getnameinfo`.
+ */
+inline void GetNameInfo(const std::shared_ptr<Loop>& loop,
+                        const std::shared_ptr<GetNameInfoReq>& req,
+                        const sockaddr& addr, int flags = 0) {
+  GetNameInfo(*loop, req, addr, flags);
+}
+
+/**
+ * Asynchronous getnameinfo(3).  The callback is called when the resolution
+ * completes, and errors are forwarded to the loop.
+ *
+ * @param loop Event loop
+ * @param callback Callback function to call when resolution completes
+ * @param addr Initialized `sockaddr_in` or `sockaddr_in6` data structure.
+ * @param flags Optional flags to modify the behavior of `getnameinfo`.
+ */
+void GetNameInfo(Loop& loop,
+                 std::function<void(const char*, const char*)> callback,
+                 const sockaddr& addr, int flags = 0);
+
+/**
+ * Asynchronous getnameinfo(3).  The callback is called when the resolution
+ * completes, and errors are forwarded to the loop.
+ *
+ * @param loop Event loop
+ * @param callback Callback function to call when resolution completes
+ * @param addr Initialized `sockaddr_in` or `sockaddr_in6` data structure.
+ * @param flags Optional flags to modify the behavior of `getnameinfo`.
+ * @return Connection object for the callback
+ */
+inline void GetNameInfo(const std::shared_ptr<Loop>& loop,
+                        std::function<void(const char*, const char*)> callback,
+                        const sockaddr& addr, int flags = 0) {
+  GetNameInfo(*loop, std::move(callback), addr, flags);
+}
+
+/**
+ * Asynchronous IPv4 getnameinfo(3).  HandleResolvedName() is called on the
+ * request when the resolution completes.  HandleError() is called on the
+ * request if any errors occur.
+ *
+ * @param loop Event loop
+ * @param req request
+ * @param ip A valid IPv4 address
+ * @param port A valid port number
+ * @param flags Optional flags to modify the behavior of `getnameinfo`.
+ */
+void GetNameInfo4(Loop& loop, const std::shared_ptr<GetNameInfoReq>& req,
+                  std::string_view ip, unsigned int port, int flags = 0);
+
+/**
+ * Asynchronous IPv4 getnameinfo(3).  HandleResolvedName() is called on the
+ * request when the resolution completes.  HandleError() is called on the
+ * request if any errors occur.
+ *
+ * @param loop Event loop
+ * @param req request
+ * @param ip A valid IPv4 address
+ * @param port A valid port number
+ * @param flags Optional flags to modify the behavior of `getnameinfo`.
+ */
+inline void GetNameInfo4(const std::shared_ptr<Loop>& loop,
+                         const std::shared_ptr<GetNameInfoReq>& req,
+                         std::string_view ip, unsigned int port,
+                         int flags = 0) {
+  return GetNameInfo4(*loop, req, ip, port, flags);
+}
+
+/**
+ * Asynchronous IPv4 getnameinfo(3).  The callback is called when the resolution
+ * completes, and errors are forwarded to the loop.
+ *
+ * @param loop Event loop
+ * @param callback Callback function to call when resolution completes
+ * @param ip A valid IPv4 address
+ * @param port A valid port number
+ * @param flags Optional flags to modify the behavior of `getnameinfo`.
+ */
+void GetNameInfo4(Loop& loop,
+                  std::function<void(const char*, const char*)> callback,
+                  std::string_view ip, unsigned int port, int flags = 0);
+
+/**
+ * Asynchronous IPv4 getnameinfo(3).  The callback is called when the resolution
+ * completes, and errors are forwarded to the loop.  This is a convenience
+ * wrapper.
+ *
+ * @param loop Event loop
+ * @param ip A valid IPv4 address
+ * @param port A valid port number
+ * @param callback Callback function to call when resolution completes
+ * @param flags Optional flags to modify the behavior of `getnameinfo`.
+ */
+inline void GetNameInfo4(const std::shared_ptr<Loop>& loop,
+                         std::function<void(const char*, const char*)> callback,
+                         std::string_view ip, unsigned int port,
+                         int flags = 0) {
+  return GetNameInfo4(*loop, std::move(callback), ip, port, flags);
+}
+
+/**
+ * Asynchronous IPv6 getnameinfo(3).  HandleResolvedName() is called on the
+ * request when the resolution completes.  HandleError() is called on the
+ * request if any errors occur.
+ *
+ * @param loop Event loop
+ * @param req request
+ * @param ip A valid IPv6 address
+ * @param port A valid port number
+ * @param flags Optional flags to modify the behavior of `getnameinfo`.
+ */
+void GetNameInfo6(Loop& loop, const std::shared_ptr<GetNameInfoReq>& req,
+                  std::string_view ip, unsigned int port, int flags = 0);
+
+/**
+ * Asynchronous IPv6 getnameinfo(3).  HandleResolvedName() is called on the
+ * request when the resolution completes.  HandleError() is called on the
+ * request if any errors occur.
+ *
+ * @param loop Event loop
+ * @param req request
+ * @param ip A valid IPv6 address
+ * @param port A valid port number
+ * @param flags Optional flags to modify the behavior of `getnameinfo`.
+ */
+inline void GetNameInfo6(const std::shared_ptr<Loop>& loop,
+                         const std::shared_ptr<GetNameInfoReq>& req,
+                         std::string_view ip, unsigned int port,
+                         int flags = 0) {
+  GetNameInfo6(*loop, req, ip, port, flags);
+}
+
+/**
+ * Asynchronous IPv6 getnameinfo(3).  The callback is called when the resolution
+ * completes, and errors are forwarded to the loop.  This is a convenience
+ * wrapper.
+ *
+ * @param loop Event loop
+ * @param callback Callback function to call when resolution completes
+ * @param ip A valid IPv6 address
+ * @param port A valid port number
+ * @param flags Optional flags to modify the behavior of `getnameinfo`.
+ */
+void GetNameInfo6(Loop& loop,
+                  std::function<void(const char*, const char*)> callback,
+                  std::string_view ip, unsigned int port, int flags = 0);
+
+/**
+ * Asynchronous IPv6 getnameinfo(3).  The callback is called when the resolution
+ * completes, and errors are forwarded to the loop.  This is a convenience
+ * wrapper.
+ *
+ * @param loop Event loop
+ * @param callback Callback function to call when resolution completes
+ * @param ip A valid IPv6 address
+ * @param port A valid port number
+ * @param flags Optional flags to modify the behavior of `getnameinfo`.
+ */
+inline void GetNameInfo6(const std::shared_ptr<Loop>& loop,
+                         std::function<void(const char*, const char*)> callback,
+                         std::string_view ip, unsigned int port,
+                         int flags = 0) {
+  return GetNameInfo6(*loop, std::move(callback), ip, port, flags);
+}
+
+}  // namespace wpi::uv
+
+#endif  // WPINET_UV_GETNAMEINFO_H_
diff --git a/wpinet/src/main/native/include/wpinet/uv/Handle.h b/wpinet/src/main/native/include/wpinet/uv/Handle.h
new file mode 100644
index 0000000..903d43c
--- /dev/null
+++ b/wpinet/src/main/native/include/wpinet/uv/Handle.h
@@ -0,0 +1,298 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#ifndef WPINET_UV_HANDLE_H_
+#define WPINET_UV_HANDLE_H_
+
+#include <uv.h>
+
+#include <cstdlib>
+#include <functional>
+#include <memory>
+#include <string_view>
+#include <utility>
+
+#include <wpi/Signal.h>
+
+#include "wpinet/uv/Buffer.h"
+#include "wpinet/uv/Error.h"
+#include "wpinet/uv/Loop.h"
+
+namespace wpi::uv {
+
+/**
+ * Handle.
+ * Handles are not moveable or copyable and cannot be directly constructed.
+ * This class provides shared_ptr ownership and shared_from_this.
+ * Use the specific handle type Create() functions to create handles.
+ */
+class Handle : public std::enable_shared_from_this<Handle> {
+ public:
+  using Type = uv_handle_type;
+
+  Handle(const Handle&) = delete;
+  Handle(Handle&&) = delete;
+  Handle& operator=(const Handle&) = delete;
+  Handle& operator=(Handle&&) = delete;
+  virtual ~Handle() noexcept;
+
+  /**
+   * Get the type of the handle.
+   *
+   * A base handle offers no functionality to promote it to the actual handle
+   * type. By means of this function, the type of the underlying handle as
+   * specified by Type is made available.
+   *
+   * @return The actual type of the handle.
+   */
+  Type GetType() const noexcept { return m_uv_handle->type; }
+
+  /**
+   * Get the name of the type of the handle.  E.g. "pipe" for pipe handles.
+   */
+  std::string_view GetTypeName() const noexcept {
+    return uv_handle_type_name(m_uv_handle->type);
+  }
+
+  /**
+   * Get the loop where this handle runs.
+   *
+   * @return The loop.
+   */
+  std::shared_ptr<Loop> GetLoop() const noexcept {
+    return GetLoopRef().shared_from_this();
+  }
+
+  /**
+   * Get the loop where this handle runs.
+   *
+   * @return The loop.
+   */
+  Loop& GetLoopRef() const noexcept {
+    return *static_cast<Loop*>(m_uv_handle->loop->data);
+  }
+
+  /**
+   * Check if the handle is active.
+   *
+   * What _active_ means depends on the type of handle:
+   *
+   * * An AsyncHandle handle is always active and cannot be deactivated,
+   * except by closing it with uv_close().
+   * * A PipeHandle, TcpHandle, UDPHandle, etc. handle - basically any handle
+   * that deals with I/O - is active when it is doing something that involves
+   * I/O, like reading, writing, connecting, accepting new connections, etc.
+   * * A CheckHandle, IdleHandle, TimerHandle, etc. handle is active when it
+   * has been started with a call to `Start()`.
+   *
+   * Rule of thumb: if a handle of type `FooHandle` has a `Start()` member
+   * method, then it’s active from the moment that method is called. Likewise,
+   * `Stop()` deactivates the handle again.
+   *
+   * @return True if the handle is active, false otherwise.
+   */
+  bool IsActive() const noexcept { return uv_is_active(m_uv_handle) != 0; }
+
+  /**
+   * Check if a handle is closing or closed.
+   *
+   * This function should only be used between the initialization of the
+   * handle and the arrival of the close callback.
+   *
+   * @return True if the handle is closing or closed, false otherwise.
+   */
+  bool IsClosing() const noexcept {
+    return m_closed || uv_is_closing(m_uv_handle) != 0;
+  }
+
+  /**
+   * Request handle to be closed.
+   *
+   * This **must** be called on each handle before memory is released.
+   * In-progress requests are cancelled and this can result in error() being
+   * emitted.
+   *
+   * The handle will emit closed() when finished.
+   */
+  void Close() noexcept;
+
+  /**
+   * Set if the loop is closing.
+   *
+   * This is set during EventLoopRunner.Stop(), and can be used for other cases
+   * to indicate the loop should be closing. For instance for a uv_walk loop can
+   * use this to close existing handles.
+   *
+   * @param loopClosing true to set the loop currently in closing stages.
+   */
+  void SetLoopClosing(bool loopClosing) noexcept {
+    m_loopClosing = loopClosing;
+  }
+
+  /**
+   * Get the loop closing status.
+   *
+   * This can be used from closed() in order to tell if a closing loop is the
+   * reason for the close, or another reason.
+   *
+   * @return true if the loop is closing, otherwise false.
+   */
+  bool IsLoopClosing() const noexcept { return m_loopClosing; }
+
+  /**
+   * Reference the given handle.
+   *
+   * References are idempotent, that is, if a handle is already referenced
+   * calling this function again will have no effect.
+   */
+  void Reference() noexcept { uv_ref(m_uv_handle); }
+
+  /**
+   * Unreference the given handle.
+   *
+   * References are idempotent, that is, if a handle is not referenced calling
+   * this function again will have no effect.
+   */
+  void Unreference() noexcept { uv_unref(m_uv_handle); }
+
+  /**
+   * Check if the given handle is referenced.
+   * @return True if the handle is referenced, false otherwise.
+   */
+  bool HasReference() const noexcept { return uv_has_ref(m_uv_handle) != 0; }
+
+  /**
+   * Return the size of the underlying handle type.
+   * @return The size of the underlying handle type.
+   */
+  size_t RawSize() const noexcept { return uv_handle_size(m_uv_handle->type); }
+
+  /**
+   * Get the underlying handle data structure.
+   *
+   * @return The underlying handle data structure.
+   */
+  uv_handle_t* GetRawHandle() const noexcept { return m_uv_handle; }
+
+  /**
+   * Set the functions used for allocating and releasing buffers.  The size
+   * passed to the allocator function is a "suggested" size--it's just an
+   * indication, not related in any way to the pending data to be read.  The
+   * user is free to allocate the amount of memory they decide.  For example,
+   * applications with custom allocation schemes may decide to use a different
+   * size which matches the memory chunks they already have for other purposes.
+   *
+   * @warning Be very careful changing the allocator after the loop has started
+   * running; there are no interlocks between this and buffers currently in
+   * flight.
+   *
+   * @param alloc Allocation function
+   * @param dealloc Deallocation function
+   */
+  void SetBufferAllocator(std::function<Buffer(size_t)> alloc,
+                          std::function<void(Buffer&)> dealloc) {
+    m_allocBuf = std::move(alloc);
+    m_freeBuf = std::move(dealloc);
+  }
+
+  /**
+   * Free a buffer.  Uses the function provided to SetBufFree() or
+   * Buffer::Deallocate by default.
+   *
+   * @param buf The buffer
+   */
+  void FreeBuf(Buffer& buf) const noexcept { m_freeBuf(buf); }
+
+  /**
+   * Gets user-defined data.
+   * @return User-defined data if any, nullptr otherwise.
+   */
+  template <typename T = void>
+  std::shared_ptr<T> GetData() const {
+    return std::static_pointer_cast<T>(m_data);
+  }
+
+  /**
+   * Sets user-defined data.
+   * @param data User-defined arbitrary data.
+   */
+  void SetData(std::shared_ptr<void> data) { m_data = std::move(data); }
+
+  /**
+   * Error signal
+   */
+  sig::Signal<Error> error;
+
+  /**
+   * Closed signal
+   */
+  sig::Signal<> closed;
+
+  /**
+   * Report an error.
+   * @param err Error code
+   */
+  void ReportError(int err) const { error(Error(err)); }
+
+ protected:
+  explicit Handle(uv_handle_t* uv_handle) : m_uv_handle{uv_handle} {
+    m_uv_handle->data = this;
+  }
+
+  void Keep() noexcept { m_self = shared_from_this(); }
+  void Release() noexcept { m_self.reset(); }
+  void ForceClosed() noexcept { m_closed = true; }
+
+  static void AllocBuf(uv_handle_t* handle, size_t size, uv_buf_t* buf);
+  static void DefaultFreeBuf(Buffer& buf);
+
+  template <typename F, typename... Args>
+  bool Invoke(F&& f, Args&&... args) const {
+    auto err = std::forward<F>(f)(std::forward<Args>(args)...);
+    if (err < 0) {
+      ReportError(err);
+    }
+    return err == 0;
+  }
+
+ private:
+  std::shared_ptr<Handle> m_self;
+  uv_handle_t* m_uv_handle;
+  bool m_closed = false;
+  bool m_loopClosing = false;
+  std::function<Buffer(size_t)> m_allocBuf{&Buffer::Allocate};
+  std::function<void(Buffer&)> m_freeBuf{&DefaultFreeBuf};
+  std::shared_ptr<void> m_data;
+};
+
+/**
+ * Handle.
+ */
+template <typename T, typename U>
+class HandleImpl : public Handle {
+ public:
+  std::shared_ptr<T> shared_from_this() {
+    return std::static_pointer_cast<T>(Handle::shared_from_this());
+  }
+
+  std::shared_ptr<const T> shared_from_this() const {
+    return std::static_pointer_cast<const T>(Handle::shared_from_this());
+  }
+
+  /**
+   * Get the underlying handle data structure.
+   *
+   * @return The underlying handle data structure.
+   */
+  U* GetRaw() const noexcept {
+    return reinterpret_cast<U*>(this->GetRawHandle());
+  }
+
+ protected:
+  HandleImpl() : Handle{static_cast<uv_handle_t*>(std::malloc(sizeof(U)))} {}
+};
+
+}  // namespace wpi::uv
+
+#endif  // WPINET_UV_HANDLE_H_
diff --git a/wpinet/src/main/native/include/wpinet/uv/Idle.h b/wpinet/src/main/native/include/wpinet/uv/Idle.h
new file mode 100644
index 0000000..4ed6d07
--- /dev/null
+++ b/wpinet/src/main/native/include/wpinet/uv/Idle.h
@@ -0,0 +1,75 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#ifndef WPINET_UV_IDLE_H_
+#define WPINET_UV_IDLE_H_
+
+#include <uv.h>
+
+#include <memory>
+
+#include <wpi/Signal.h>
+
+#include "wpinet/uv/Handle.h"
+
+namespace wpi::uv {
+
+class Loop;
+
+/**
+ * Idle handle.
+ *
+ * Idle handles will generate a signal once per loop iteration, right
+ * before the Prepare handles.
+ *
+ * The notable difference with Prepare handles is that when there are active
+ * idle handles, the loop will perform a zero timeout poll instead of blocking
+ * for I/O.
+ *
+ * @warning Despite the name, idle handles will signal every loop iteration,
+ * not when the loop is actually "idle".  This also means they can easly become
+ * CPU hogs.
+ */
+class Idle final : public HandleImpl<Idle, uv_idle_t> {
+  struct private_init {};
+
+ public:
+  explicit Idle(const private_init&) {}
+  ~Idle() noexcept override = default;
+
+  /**
+   * Create an idle handle.
+   *
+   * @param loop Loop object where this handle runs.
+   */
+  static std::shared_ptr<Idle> Create(Loop& loop);
+
+  /**
+   * Create an idle handle.
+   *
+   * @param loop Loop object where this handle runs.
+   */
+  static std::shared_ptr<Idle> Create(const std::shared_ptr<Loop>& loop) {
+    return Create(*loop);
+  }
+
+  /**
+   * Start the handle.
+   */
+  void Start();
+
+  /**
+   * Stop the handle.  The signal will no longer be generated.
+   */
+  void Stop() { Invoke(&uv_idle_stop, GetRaw()); }
+
+  /**
+   * Signal generated once per loop iteration prior to Prepare signals.
+   */
+  sig::Signal<> idle;
+};
+
+}  // namespace wpi::uv
+
+#endif  // WPINET_UV_IDLE_H_
diff --git a/wpinet/src/main/native/include/wpinet/uv/Loop.h b/wpinet/src/main/native/include/wpinet/uv/Loop.h
new file mode 100644
index 0000000..129faf5
--- /dev/null
+++ b/wpinet/src/main/native/include/wpinet/uv/Loop.h
@@ -0,0 +1,254 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#ifndef WPINET_UV_LOOP_H_
+#define WPINET_UV_LOOP_H_
+
+#include <uv.h>
+
+#include <atomic>
+#include <chrono>
+#include <functional>
+#include <memory>
+#include <thread>
+#include <utility>
+
+#include <wpi/Signal.h>
+#include <wpi/function_ref.h>
+
+#include "wpinet/uv/Error.h"
+
+namespace wpi::uv {
+
+class Handle;
+
+/**
+ * Event loop.
+ *
+ * The event loop is the central part of uv functionality.  It takes care of
+ * polling for I/O and scheduling signals to be generated based on different
+ * sources of events.
+ *
+ * The event loop is not moveable, copyable, or directly constructible.  Use
+ * Create() to create an event loop, or GetDefault() to get the default loop
+ * if you know your program will only have a single one.
+ */
+class Loop final : public std::enable_shared_from_this<Loop> {
+  struct private_init {};
+
+ public:
+  using Time = std::chrono::duration<uint64_t, std::milli>;
+
+  enum Mode {
+    kDefault = UV_RUN_DEFAULT,
+    kOnce = UV_RUN_ONCE,
+    kNoWait = UV_RUN_NOWAIT
+  };
+
+  explicit Loop(const private_init&) noexcept;
+
+  Loop(const Loop&) = delete;
+  Loop& operator=(const Loop&) = delete;
+  Loop(Loop&& oth) = delete;
+  Loop& operator=(Loop&& oth) = delete;
+
+  ~Loop() noexcept;
+
+  /**
+   * Create a new event loop.  The created loop is not the default event loop.
+   *
+   * @return The newly created loop.  May return nullptr if a failure occurs.
+   */
+  static std::shared_ptr<Loop> Create();
+
+  /**
+   * Create the default event loop.  Only use this event loop if a single loop
+   * is needed for the entire application.
+   *
+   * @return The newly created loop.  May return nullptr if a failure occurs.
+   */
+  static std::shared_ptr<Loop> GetDefault();
+
+  /**
+   * Release all internal loop resources.
+   *
+   * Call this function only when the loop has finished executing and all open
+   * handles and requests have been closed, or the loop will emit an error.
+   *
+   * error() will be emitted in case of errors.
+   */
+  void Close();
+
+  /**
+   * Run the event loop.
+   *
+   * Available modes are:
+   *
+   * * `Loop::kDefault`: Run the event loop until there are no
+   *                     active and referenced handles or requests.
+   * * `Loop::kOnce`: Run a single event loop iteration. Note that this
+   *                  function blocksif there are no pending callbacks.
+   * * `Loop::kNoWait`: Run a single event loop iteration, but don't block
+   *                    if there are no pending callbacks.
+   *
+   * @return True when done, false in all other cases.
+   */
+  bool Run(Mode mode = kDefault) {
+    m_tid = std::this_thread::get_id();
+    int rv = uv_run(m_loop, static_cast<uv_run_mode>(static_cast<int>(mode)));
+    m_tid = std::thread::id{};
+    return rv == 0;
+  }
+
+  /**
+   * Check if there are active resources.
+   *
+   * @return True if there are active resources in the loop.
+   */
+  bool IsAlive() const noexcept { return uv_loop_alive(m_loop) != 0; }
+
+  /**
+   * Stop the event loop.
+   *
+   * This will cause Run() to end as soon as possible.
+   * This will happen not sooner than the next loop iteration.
+   * If this function was called before blocking for I/O, the loop won’t block
+   * for I/O on this iteration.
+   */
+  void Stop() noexcept { uv_stop(m_loop); }
+
+  /**
+   * Get backend file descriptor.
+   *
+   * Only kqueue, epoll and event ports are supported.
+   * This can be used in conjunction with `run(Loop::kNoWait)` to poll
+   * in one thread and run the event loop’s callbacks in another.
+   *
+   * @return The backend file descriptor.
+   */
+  int GetDescriptor() const noexcept { return uv_backend_fd(m_loop); }
+
+  /**
+   * Get the poll timeout.
+   *
+   * @return A `std::pair` composed of a boolean value that is true in case of
+   * valid timeout, false otherwise, and the timeout
+   * (`std::chrono::duration<uint64_t, std::milli>`).
+   */
+  std::pair<bool, Time> GetTimeout() const noexcept {
+    auto to = uv_backend_timeout(m_loop);
+    return std::make_pair(to == -1, Time{to});
+  }
+
+  /**
+   * Return the current timestamp in milliseconds.
+   *
+   * The timestamp is cached at the start of the event loop tick.
+   * The timestamp increases monotonically from some arbitrary point in
+   * time.
+   * Don’t make assumptions about the starting point, you will only get
+   * disappointed.
+   *
+   * @return The current timestamp in milliseconds (actual type is
+   * `std::chrono::duration<uint64_t, std::milli>`).
+   */
+  Time Now() const noexcept { return Time{uv_now(m_loop)}; }
+
+  /**
+   * Update the event loop’s concept of _now_.
+   *
+   * The current time is cached at the start of the event loop tick in order
+   * to reduce the number of time-related system calls.
+   * You won’t normally need to call this function unless you have callbacks
+   * that block the event loop for longer periods of time, where _longer_ is
+   * somewhat subjective but probably on the order of a millisecond or more.
+   */
+  void UpdateTime() noexcept { uv_update_time(m_loop); }
+
+  /**
+   * Walk the list of handles.
+   *
+   * The callback will be executed once for each handle that is still active.
+   *
+   * @param callback A function to be invoked once for each active handle.
+   */
+  void Walk(function_ref<void(Handle&)> callback);
+
+  /**
+   * Reinitialize any kernel state necessary in the child process after
+   * a fork(2) system call.
+   *
+   * Previously started watchers will continue to be started in the child
+   * process.
+   *
+   * It is necessary to explicitly call this function on every event loop
+   * created in the parent process that you plan to continue to use in the
+   * child, including the default loop (even if you don’t continue to use it
+   * in the parent). This function must be called before calling any API
+   * function using the loop in the child. Failure to do so will result in
+   * undefined behaviour, possibly including duplicate events delivered to
+   * both parent and child or aborting the child process.
+   *
+   * When possible, it is preferred to create a new loop in the child process
+   * instead of reusing a loop created in the parent. New loops created in the
+   * child process after the fork should not use this function.
+   *
+   * Note that this function is not implemented on Windows.
+   * Note also that this function is experimental in `libuv`. It may contain
+   * bugs, and is subject to change or removal. API and ABI stability is not
+   * guaranteed.
+   *
+   * error() will be emitted in case of errors.
+   */
+  void Fork();
+
+  /**
+   * Get the underlying event loop data structure.
+   *
+   * @return The underlying event loop data structure.
+   */
+  uv_loop_t* GetRaw() const noexcept { return m_loop; }
+
+  /**
+   * Gets user-defined data.
+   * @return User-defined data if any, nullptr otherwise.
+   */
+  template <typename T = void>
+  std::shared_ptr<T> GetData() const {
+    return std::static_pointer_cast<T>(m_data);
+  }
+
+  /**
+   * Sets user-defined data.
+   * @param data User-defined arbitrary data.
+   */
+  void SetData(std::shared_ptr<void> data) { m_data = std::move(data); }
+
+  /**
+   * Get the thread id of the loop thread.  If the loop is not currently
+   * running, returns default-constructed thread id.
+   */
+  std::thread::id GetThreadId() const { return m_tid; }
+
+  /**
+   * Error signal
+   */
+  sig::Signal<Error> error;
+
+  /**
+   * Reports error.
+   * @param err Error code
+   */
+  void ReportError(int err) { error(Error(err)); }
+
+ private:
+  std::shared_ptr<void> m_data;
+  uv_loop_t* m_loop;
+  uv_loop_t m_loopStruct;
+  std::atomic<std::thread::id> m_tid;
+};
+
+}  // namespace wpi::uv
+
+#endif  // WPINET_UV_LOOP_H_
diff --git a/wpinet/src/main/native/include/wpinet/uv/NetworkStream.h b/wpinet/src/main/native/include/wpinet/uv/NetworkStream.h
new file mode 100644
index 0000000..42b0a8a
--- /dev/null
+++ b/wpinet/src/main/native/include/wpinet/uv/NetworkStream.h
@@ -0,0 +1,154 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#ifndef WPINET_UV_NETWORKSTREAM_H_
+#define WPINET_UV_NETWORKSTREAM_H_
+
+#include <uv.h>
+
+#include <cstdlib>
+#include <functional>
+#include <memory>
+
+#include <wpi/Signal.h>
+
+#include "wpinet/uv/Stream.h"
+
+namespace wpi::uv {
+
+class NetworkStream;
+
+/**
+ * Connection request.
+ */
+class ConnectReq : public RequestImpl<ConnectReq, uv_connect_t> {
+ public:
+  ConnectReq();
+
+  NetworkStream& GetStream() const {
+    return *static_cast<NetworkStream*>(GetRaw()->handle->data);
+  }
+
+  /**
+   * Connection completed signal.
+   */
+  sig::Signal<> connected;
+};
+
+/**
+ * Network stream handle.
+ * This is an abstract type; there are two network stream implementations (Tcp
+ * and Pipe).
+ */
+class NetworkStream : public Stream {
+ public:
+  static constexpr int kDefaultBacklog = 128;
+
+  std::shared_ptr<NetworkStream> shared_from_this() {
+    return std::static_pointer_cast<NetworkStream>(Handle::shared_from_this());
+  }
+
+  std::shared_ptr<const NetworkStream> shared_from_this() const {
+    return std::static_pointer_cast<const NetworkStream>(
+        Handle::shared_from_this());
+  }
+
+  /**
+   * Start listening for incoming connections.  When a new incoming connection
+   * is received the connection signal is generated.
+   * @param backlog the number of connections the kernel might queue, same as
+   *        listen(2).
+   */
+  void Listen(int backlog = kDefaultBacklog);
+
+  /**
+   * Start listening for incoming connections.  This is a convenience wrapper
+   * around `Listen(int)` that also connects a callback to the connection
+   * signal.  When a new incoming connection is received the connection signal
+   * is generated (and the callback is called).
+   * @param callback the callback to call when a connection is received.
+   *        `Accept()` should be called from this callback.
+   * @param backlog the number of connections the kernel might queue, same as
+   *        listen(2).
+   */
+  void Listen(std::function<void()> callback, int backlog = kDefaultBacklog);
+
+  /**
+   * Accept incoming connection.
+   *
+   * This call is used in conjunction with `Listen()` to accept incoming
+   * connections. Call this function after receiving a ListenEvent event to
+   * accept the connection.
+   * An error signal will be emitted in case of errors.
+   *
+   * When the connection signal is emitted it is guaranteed that this
+   * function will complete successfully the first time. If you attempt to use
+   * it more than once, it may fail.
+   * It is suggested to only call this function once per connection signal.
+   *
+   * @return The stream handle for the accepted connection, or nullptr on error.
+   */
+  std::shared_ptr<NetworkStream> Accept() {
+    return DoAccept()->shared_from_this();
+  }
+
+  /**
+   * Accept incoming connection.
+   *
+   * This call is used in conjunction with `Listen()` to accept incoming
+   * connections. Call this function after receiving a connection signal to
+   * accept the connection.
+   * An error signal will be emitted in case of errors.
+   *
+   * When the connection signal is emitted it is guaranteed that this
+   * function will complete successfully the first time. If you attempt to use
+   * it more than once, it may fail.
+   * It is suggested to only call this function once per connection signal.
+   *
+   * @param client Client stream object.
+   * @return False on error.
+   */
+  bool Accept(const std::shared_ptr<NetworkStream>& client) {
+    return Invoke(&uv_accept, GetRawStream(), client->GetRawStream());
+  }
+
+  /**
+   * Signal generated when an incoming connection is received.
+   */
+  sig::Signal<> connection;
+
+ protected:
+  explicit NetworkStream(uv_stream_t* uv_stream) : Stream{uv_stream} {}
+
+  virtual NetworkStream* DoAccept() = 0;
+};
+
+template <typename T, typename U>
+class NetworkStreamImpl : public NetworkStream {
+ public:
+  std::shared_ptr<T> shared_from_this() {
+    return std::static_pointer_cast<T>(Handle::shared_from_this());
+  }
+
+  std::shared_ptr<const T> shared_from_this() const {
+    return std::static_pointer_cast<const T>(Handle::shared_from_this());
+  }
+
+  /**
+   * Get the underlying handle data structure.
+   *
+   * @return The underlying handle data structure.
+   */
+  U* GetRaw() const noexcept {
+    return reinterpret_cast<U*>(this->GetRawHandle());
+  }
+
+ protected:
+  NetworkStreamImpl()
+      : NetworkStream{static_cast<uv_stream_t*>(std::malloc(sizeof(U)))} {}
+};
+
+}  // namespace wpi::uv
+
+#endif  // WPINET_UV_NETWORKSTREAM_H_
diff --git a/wpinet/src/main/native/include/wpinet/uv/Pipe.h b/wpinet/src/main/native/include/wpinet/uv/Pipe.h
new file mode 100644
index 0000000..d7ab044
--- /dev/null
+++ b/wpinet/src/main/native/include/wpinet/uv/Pipe.h
@@ -0,0 +1,208 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#ifndef WPINET_UV_PIPE_H_
+#define WPINET_UV_PIPE_H_
+
+#include <uv.h>
+
+#include <functional>
+#include <memory>
+#include <string>
+#include <string_view>
+
+#include "wpinet/uv/NetworkStream.h"
+
+namespace wpi::uv {
+
+class Loop;
+class PipeConnectReq;
+
+/**
+ * Pipe handle.
+ * Pipe handles provide an abstraction over local domain sockets on Unix and
+ * named pipes on Windows.
+ */
+class Pipe final : public NetworkStreamImpl<Pipe, uv_pipe_t> {
+  struct private_init {};
+
+ public:
+  explicit Pipe(const private_init&) {}
+  ~Pipe() noexcept override = default;
+
+  /**
+   * Create a pipe handle.
+   *
+   * @param loop Loop object where this handle runs.
+   * @param ipc Indicates if this pipe will be used for handle passing between
+   *            processes.
+   */
+  static std::shared_ptr<Pipe> Create(Loop& loop, bool ipc = false);
+
+  /**
+   * Create a pipe handle.
+   *
+   * @param loop Loop object where this handle runs.
+   * @param ipc Indicates if this pipe will be used for handle passing between
+   *            processes.
+   */
+  static std::shared_ptr<Pipe> Create(const std::shared_ptr<Loop>& loop,
+                                      bool ipc = false) {
+    return Create(*loop, ipc);
+  }
+
+  /**
+   * Reuse this handle.  This closes the handle, and after the close completes,
+   * reinitializes it (identically to Create) and calls the provided callback.
+   * Unlike Close(), it does NOT emit the closed signal, however, IsClosing()
+   * will return true until the callback is called.  This does nothing if
+   * IsClosing() is true (e.g. if Close() was called).
+   *
+   * @param ipc IPC
+   * @param callback Callback
+   */
+  void Reuse(std::function<void()> callback, bool ipc = false);
+
+  /**
+   * Accept incoming connection.
+   *
+   * This call is used in conjunction with `Listen()` to accept incoming
+   * connections. Call this function after receiving a ListenEvent event to
+   * accept the connection.
+   * An error signal will be emitted in case of errors.
+   *
+   * When the connection signal is emitted it is guaranteed that this
+   * function will complete successfully the first time. If you attempt to use
+   * it more than once, it may fail.
+   * It is suggested to only call this function once per connection signal.
+   *
+   * @return The stream handle for the accepted connection, or nullptr on error.
+   */
+  std::shared_ptr<Pipe> Accept();
+
+  /**
+   * Accept incoming connection.
+   *
+   * This call is used in conjunction with `Listen()` to accept incoming
+   * connections. Call this function after receiving a connection signal to
+   * accept the connection.
+   * An error signal will be emitted in case of errors.
+   *
+   * When the connection signal is emitted it is guaranteed that this
+   * function will complete successfully the first time. If you attempt to use
+   * it more than once, it may fail.
+   * It is suggested to only call this function once per connection signal.
+   *
+   * @param client Client stream object.
+   * @return False on error.
+   */
+  bool Accept(const std::shared_ptr<Pipe>& client) {
+    return NetworkStream::Accept(client);
+  }
+
+  /**
+   * Open an existing file descriptor or HANDLE as a pipe.
+   *
+   * @note The passed file descriptor or HANDLE is not checked for its type, but
+   * it's required that it represents a valid pipe.
+   *
+   * @param file A valid file handle (either a file descriptor or a HANDLE).
+   */
+  void Open(uv_file file) { Invoke(&uv_pipe_open, GetRaw(), file); }
+
+  /**
+   * Bind the pipe to a file path (Unix) or a name (Windows).
+   *
+   * @note Paths on Unix get truncated to `sizeof(sockaddr_un.sun_path)` bytes,
+   * typically between 92 and 108 bytes.
+   *
+   * @param name File path (Unix) or name (Windows).
+   */
+  void Bind(std::string_view name);
+
+  /**
+   * Connect to the Unix domain socket or the named pipe.
+   *
+   * @note Paths on Unix get truncated to `sizeof(sockaddr_un.sun_path)` bytes,
+   * typically between 92 and 108 bytes.
+   *
+   * HandleConnected() is called on the request when the connection has been
+   * established.
+   * HandleError() is called on the request in case of errors during the
+   * connection.
+   *
+   * @param name File path (Unix) or name (Windows).
+   * @param req connection request
+   */
+  void Connect(std::string_view name,
+               const std::shared_ptr<PipeConnectReq>& req);
+
+  /**
+   * Connect to the Unix domain socket or the named pipe.
+   *
+   * @note Paths on Unix get truncated to `sizeof(sockaddr_un.sun_path)` bytes,
+   * typically between 92 and 108 bytes.
+   *
+   * The callback is called when the connection has been established.  Errors
+   * are reported to the stream error handler.
+   *
+   * @param name File path (Unix) or name (Windows).
+   * @param callback Callback function to call when connection established
+   */
+  void Connect(std::string_view name, std::function<void()> callback);
+
+  /**
+   * Get the name of the Unix domain socket or the named pipe.
+   * @return The name (will be empty if an error occurred).
+   */
+  std::string GetSock();
+
+  /**
+   * Get the name of the Unix domain socket or the named pipe to which the
+   * handle is connected.
+   * @return The name (will be empty if an error occurred).
+   */
+  std::string GetPeer();
+
+  /**
+   * Set the number of pending pipe instance handles when the pipe server is
+   * waiting for connections.
+   * @note This setting applies to Windows only.
+   * @param count Number of pending handles.
+   */
+  void SetPendingInstances(int count) {
+    uv_pipe_pending_instances(GetRaw(), count);
+  }
+
+  /**
+   * Alters pipe permissions, allowing it to be accessed from processes run
+   * by different users.  Makes the pipe writable or readable by all users.
+   * Mode can be UV_WRITABLE, UV_READABLE, or both.  This function is blocking.
+   * @param flags chmod flags
+   */
+  void Chmod(int flags) { Invoke(&uv_pipe_chmod, GetRaw(), flags); }
+
+ private:
+  Pipe* DoAccept() override;
+
+  struct ReuseData {
+    std::function<void()> callback;
+    bool ipc;
+  };
+  std::unique_ptr<ReuseData> m_reuseData;
+};
+
+/**
+ * Pipe connection request.
+ */
+class PipeConnectReq : public ConnectReq {
+ public:
+  Pipe& GetStream() const {
+    return *static_cast<Pipe*>(&ConnectReq::GetStream());
+  }
+};
+
+}  // namespace wpi::uv
+
+#endif  // WPINET_UV_PIPE_H_
diff --git a/wpinet/src/main/native/include/wpinet/uv/Poll.h b/wpinet/src/main/native/include/wpinet/uv/Poll.h
new file mode 100644
index 0000000..1e63836
--- /dev/null
+++ b/wpinet/src/main/native/include/wpinet/uv/Poll.h
@@ -0,0 +1,122 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#ifndef WPINET_UV_POLL_H_
+#define WPINET_UV_POLL_H_
+
+#include <uv.h>
+
+#include <memory>
+
+#include <wpi/Signal.h>
+
+#include "wpinet/uv/Handle.h"
+
+namespace wpi::uv {
+
+class Loop;
+
+/**
+ * Poll handle.
+ */
+class Poll final : public HandleImpl<Poll, uv_poll_t> {
+  struct private_init {};
+
+ public:
+  explicit Poll(const private_init&) {}
+  ~Poll() noexcept override = default;
+
+  /**
+   * Create a poll handle using a file descriptor.
+   *
+   * @param loop Loop object where this handle runs.
+   * @param fd File descriptor.
+   */
+  static std::shared_ptr<Poll> Create(Loop& loop, int fd);
+
+  /**
+   * Create a poll handle using a file descriptor.
+   *
+   * @param loop Loop object where this handle runs.
+   * @param fd File descriptor.
+   */
+  static std::shared_ptr<Poll> Create(const std::shared_ptr<Loop>& loop,
+                                      int fd) {
+    return Create(*loop, fd);
+  }
+
+  /**
+   * Create a poll handle using a socket descriptor.
+   *
+   * @param loop Loop object where this handle runs.
+   * @param sock Socket descriptor.
+   */
+  static std::shared_ptr<Poll> CreateSocket(Loop& loop, uv_os_sock_t sock);
+
+  /**
+   * Create a poll handle using a socket descriptor.
+   *
+   * @param loop Loop object where this handle runs.
+   * @param sock Socket descriptor.
+   */
+  static std::shared_ptr<Poll> CreateSocket(const std::shared_ptr<Loop>& loop,
+                                            uv_os_sock_t sock) {
+    return CreateSocket(*loop, sock);
+  }
+
+  /**
+   * Reuse this handle.  This closes the handle, and after the close completes,
+   * reinitializes it (identically to Create) and calls the provided callback.
+   * Unlike Close(), it does NOT emit the closed signal, however, IsClosing()
+   * will return true until the callback is called.  This does nothing if
+   * IsClosing() is true (e.g. if Close() was called).
+   *
+   * @param fd File descriptor
+   * @param callback Callback
+   */
+  void Reuse(int fd, std::function<void()> callback);
+
+  /**
+   * Reuse this handle.  This closes the handle, and after the close completes,
+   * reinitializes it (identically to CreateSocket) and calls the provided
+   * callback.  Unlike Close(), it does NOT emit the closed signal, however,
+   * IsClosing() will return true until the callback is called.  This does
+   * nothing if IsClosing() is true (e.g. if Close() was called).
+   *
+   * @param sock Socket descriptor.
+   * @param callback Callback
+   */
+  void ReuseSocket(uv_os_sock_t sock, std::function<void()> callback);
+
+  /**
+   * Start polling the file descriptor.
+   *
+   * @param events Bitmask of events (UV_READABLE, UV_WRITEABLE, UV_PRIORITIZED,
+   *               and UV_DISCONNECT).
+   */
+  void Start(int events);
+
+  /**
+   * Stop polling the file descriptor.
+   */
+  void Stop() { Invoke(&uv_poll_stop, GetRaw()); }
+
+  /**
+   * Signal generated when a poll event occurs.
+   */
+  sig::Signal<int> pollEvent;
+
+ private:
+  struct ReuseData {
+    std::function<void()> callback;
+    bool isSocket;
+    int fd;
+    uv_os_sock_t sock;
+  };
+  std::unique_ptr<ReuseData> m_reuseData;
+};
+
+}  // namespace wpi::uv
+
+#endif  // WPINET_UV_POLL_H_
diff --git a/wpinet/src/main/native/include/wpinet/uv/Prepare.h b/wpinet/src/main/native/include/wpinet/uv/Prepare.h
new file mode 100644
index 0000000..e19dd30
--- /dev/null
+++ b/wpinet/src/main/native/include/wpinet/uv/Prepare.h
@@ -0,0 +1,66 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#ifndef WPINET_UV_PREPARE_H_
+#define WPINET_UV_PREPARE_H_
+
+#include <uv.h>
+
+#include <memory>
+
+#include <wpi/Signal.h>
+
+#include "wpinet/uv/Handle.h"
+
+namespace wpi::uv {
+
+class Loop;
+
+/**
+ * Prepare handle.
+ * Prepare handles will generate a signal once per loop iteration, right
+ * before polling for I/O.
+ */
+class Prepare final : public HandleImpl<Prepare, uv_prepare_t> {
+  struct private_init {};
+
+ public:
+  explicit Prepare(const private_init&) {}
+  ~Prepare() noexcept override = default;
+
+  /**
+   * Create a prepare handle.
+   *
+   * @param loop Loop object where this handle runs.
+   */
+  static std::shared_ptr<Prepare> Create(Loop& loop);
+
+  /**
+   * Create a prepare handle.
+   *
+   * @param loop Loop object where this handle runs.
+   */
+  static std::shared_ptr<Prepare> Create(const std::shared_ptr<Loop>& loop) {
+    return Create(*loop);
+  }
+
+  /**
+   * Start the handle.
+   */
+  void Start();
+
+  /**
+   * Stop the handle.  The signal will no longer be generated.
+   */
+  void Stop() { Invoke(&uv_prepare_stop, GetRaw()); }
+
+  /**
+   * Signal generated once per loop iteration prior to polling for I/O.
+   */
+  sig::Signal<> prepare;
+};
+
+}  // namespace wpi::uv
+
+#endif  // WPINET_UV_PREPARE_H_
diff --git a/wpinet/src/main/native/include/wpinet/uv/Process.h b/wpinet/src/main/native/include/wpinet/uv/Process.h
new file mode 100644
index 0000000..3d84bde
--- /dev/null
+++ b/wpinet/src/main/native/include/wpinet/uv/Process.h
@@ -0,0 +1,311 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#ifndef WPINET_UV_PROCESS_H_
+#define WPINET_UV_PROCESS_H_
+
+#include <uv.h>
+
+#include <initializer_list>
+#include <memory>
+#include <span>
+#include <string>
+#include <string_view>
+
+#include <wpi/Signal.h>
+#include <wpi/SmallVector.h>
+
+#include "wpinet/uv/Handle.h"
+
+namespace wpi::uv {
+
+class Loop;
+class Pipe;
+
+/**
+ * Process handle.
+ * Process handles will spawn a new process and allow the user to control it
+ * and establish communication channels with it using streams.
+ */
+class Process final : public HandleImpl<Process, uv_process_t> {
+  struct private_init {};
+
+ public:
+  explicit Process(const private_init&) {}
+  ~Process() noexcept override = default;
+
+  /**
+   * Structure for Spawn() option temporaries.  This is a reference type, so if
+   * this value is stored outside of a temporary, be careful about overwriting
+   * what it points to.
+   */
+  struct Option {
+    enum Type {
+      kNone,
+      kArg,
+      kEnv,
+      kCwd,
+      kUid,
+      kGid,
+      kSetFlags,
+      kClearFlags,
+      kStdioIgnore,
+      kStdioInheritFd,
+      kStdioInheritPipe,
+      kStdioCreatePipe
+    };
+
+    Option() : m_type(kNone) {}
+
+    /*implicit*/ Option(const char* arg) {  // NOLINT
+      m_data.str = arg;
+    }
+
+    /*implicit*/ Option(const std::string& arg) {  // NOLINT
+      m_data.str = arg.data();
+    }
+
+    /*implicit*/ Option(std::string_view arg)  // NOLINT
+        : m_strData(arg) {
+      m_data.str = m_strData.c_str();
+    }
+
+    /*implicit*/ Option(const SmallVectorImpl<char>& arg)  // NOLINT
+        : m_strData(arg.data(), arg.size()) {
+      m_data.str = m_strData.c_str();
+    }
+
+    explicit Option(Type type) : m_type(type) {}
+
+    Type m_type = kArg;
+    std::string m_strData;
+    union {
+      const char* str;
+      uv_uid_t uid;
+      uv_gid_t gid;
+      unsigned int flags;
+      struct {
+        size_t index;
+        union {
+          int fd;
+          Pipe* pipe;
+        };
+        unsigned int flags;
+      } stdio;
+    } m_data;
+  };
+
+  /**
+   * Set environment variable for the subprocess.  If not set, the parent's
+   * environment is used.
+   * @param env environment variable
+   */
+  static Option Env(std::string_view env) {
+    Option o(Option::kEnv);
+    o.m_strData = env;
+    o.m_data.str = o.m_strData.c_str();
+    return o;
+  }
+
+  /**
+   * Set the current working directory for the subprocess.
+   * @param cwd current working directory
+   */
+  static Option Cwd(std::string_view cwd) {
+    Option o(Option::kCwd);
+    o.m_strData = cwd;
+    o.m_data.str = o.m_strData.c_str();
+    return o;
+  }
+
+  /**
+   * Set the child process' user id.
+   * @param uid user id
+   */
+  static Option Uid(uv_uid_t uid) {
+    Option o(Option::kUid);
+    o.m_data.uid = uid;
+    return o;
+  }
+
+  /**
+   * Set the child process' group id.
+   * @param gid group id
+   */
+  static Option Gid(uv_gid_t gid) {
+    Option o(Option::kGid);
+    o.m_data.gid = gid;
+    return o;
+  }
+
+  /**
+   * Set spawn flags.
+   * @param flags Bitmask values from uv_process_flags.
+   */
+  static Option SetFlags(unsigned int flags) {
+    Option o(Option::kSetFlags);
+    o.m_data.flags = flags;
+    return o;
+  }
+
+  /**
+   * Clear spawn flags.
+   * @param flags Bitmask values from uv_process_flags.
+   */
+  static Option ClearFlags(unsigned int flags) {
+    Option o(Option::kClearFlags);
+    o.m_data.flags = flags;
+    return o;
+  }
+
+  /**
+   * Explicitly ignore a stdio.
+   * @param index stdio index
+   */
+  static Option StdioIgnore(size_t index) {
+    Option o(Option::kStdioIgnore);
+    o.m_data.stdio.index = index;
+    return o;
+  }
+
+  /**
+   * Inherit a file descriptor from the parent process.
+   * @param index stdio index
+   * @param fd parent file descriptor
+   */
+  static Option StdioInherit(size_t index, int fd) {
+    Option o(Option::kStdioInheritFd);
+    o.m_data.stdio.index = index;
+    o.m_data.stdio.fd = fd;
+    return o;
+  }
+
+  /**
+   * Inherit a pipe from the parent process.
+   * @param index stdio index
+   * @param pipe pipe
+   */
+  static Option StdioInherit(size_t index, Pipe& pipe) {
+    Option o(Option::kStdioInheritPipe);
+    o.m_data.stdio.index = index;
+    o.m_data.stdio.pipe = &pipe;
+    return o;
+  }
+
+  /**
+   * Create a pipe between the child and the parent.
+   * @param index stdio index
+   * @param pipe pipe
+   * @param flags Some combination of UV_READABLE_PIPE, UV_WRITABLE_PIPE, and
+   *              UV_OVERLAPPED_PIPE (Windows only, ignored on Unix).
+   */
+  static Option StdioCreatePipe(size_t index, Pipe& pipe, unsigned int flags) {
+    Option o(Option::kStdioCreatePipe);
+    o.m_data.stdio.index = index;
+    o.m_data.stdio.pipe = &pipe;
+    o.m_data.stdio.flags = flags;
+    return o;
+  }
+
+  /**
+   * Disables inheritance for file descriptors / handles that this process
+   * inherited from its parent.  The effect is that child processes spawned
+   * by this process don't accidentally inherit these handles.
+   *
+   * It is recommended to call this function as early in your program as
+   * possible, before the inherited file descriptors can be closed or
+   * duplicated.
+   */
+  static void DisableStdioInheritance() { uv_disable_stdio_inheritance(); }
+
+  /**
+   * Starts a process.  If the process is not successfully spawned, an error
+   * is generated on the loop and this function returns nullptr.
+   *
+   * Possible reasons for failing to spawn would include (but not be limited to)
+   * the file to execute not existing, not having permissions to use the setuid
+   * or setgid specified, or not having enough memory to allocate for the new
+   * process.
+   *
+   * @param loop Loop object where this handle runs.
+   * @param file Path pointing to the program to be executed
+   * @param options Process options
+   */
+  static std::shared_ptr<Process> SpawnArray(Loop& loop, std::string_view file,
+                                             std::span<const Option> options);
+
+  static std::shared_ptr<Process> SpawnArray(
+      Loop& loop, std::string_view file,
+      std::initializer_list<Option> options) {
+    return SpawnArray(loop, file, {options.begin(), options.end()});
+  }
+
+  template <typename... Args>
+  static std::shared_ptr<Process> Spawn(Loop& loop, std::string_view file,
+                                        const Args&... options) {
+    return SpawnArray(loop, file, {options...});
+  }
+
+  /**
+   * Starts a process.  If the process is not successfully spawned, an error
+   * is generated on the loop and this function returns nullptr.
+   *
+   * Possible reasons for failing to spawn would include (but not be limited to)
+   * the file to execute not existing, not having permissions to use the setuid
+   * or setgid specified, or not having enough memory to allocate for the new
+   * process.
+   *
+   * @param loop Loop object where this handle runs.
+   * @param file Path pointing to the program to be executed
+   * @param options Process options
+   */
+  static std::shared_ptr<Process> SpawnArray(const std::shared_ptr<Loop>& loop,
+                                             std::string_view file,
+                                             std::span<const Option> options) {
+    return SpawnArray(*loop, file, options);
+  }
+
+  static std::shared_ptr<Process> SpawnArray(
+      const std::shared_ptr<Loop>& loop, std::string_view file,
+      std::initializer_list<Option> options) {
+    return SpawnArray(*loop, file, options);
+  }
+
+  template <typename... Args>
+  static std::shared_ptr<Process> Spawn(const std::shared_ptr<Loop>& loop,
+                                        std::string_view file,
+                                        const Args&... options) {
+    return SpawnArray(*loop, file, {options...});
+  }
+
+  /**
+   * Sends the specified signal to the process.
+   * @param signum signal number
+   */
+  void Kill(int signum) { Invoke(&uv_process_kill, GetRaw(), signum); }
+
+  /**
+   * Sends the specified signal to the given PID.
+   * @param pid process ID
+   * @param signum signal number
+   * @return 0 on success, otherwise error code.
+   */
+  static int Kill(int pid, int signum) noexcept { return uv_kill(pid, signum); }
+
+  /**
+   * Get the process ID.
+   * @return Process ID.
+   */
+  uv_pid_t GetPid() const noexcept { return GetRaw()->pid; }
+
+  /**
+   * Signal generated when the process exits.  The parameters are the exit
+   * status and the signal that caused the process to terminate, if any.
+   */
+  sig::Signal<int64_t, int> exited;
+};
+
+}  // namespace wpi::uv
+
+#endif  // WPINET_UV_PROCESS_H_
diff --git a/wpinet/src/main/native/include/wpinet/uv/Request.h b/wpinet/src/main/native/include/wpinet/uv/Request.h
new file mode 100644
index 0000000..3fbec6f
--- /dev/null
+++ b/wpinet/src/main/native/include/wpinet/uv/Request.h
@@ -0,0 +1,166 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#ifndef WPINET_UV_REQUEST_H_
+#define WPINET_UV_REQUEST_H_
+
+#include <uv.h>
+
+#include <functional>
+#include <memory>
+
+#include "wpinet/uv/Error.h"
+
+namespace wpi::uv {
+
+/**
+ * Request.  Requests are not moveable or copyable.
+ * This class provides shared_ptr ownership and shared_from_this.
+ */
+class Request : public std::enable_shared_from_this<Request> {
+ public:
+  using Type = uv_req_type;
+
+  Request(const Request&) = delete;
+  Request(Request&&) = delete;
+  Request& operator=(const Request&) = delete;
+  Request& operator=(Request&&) = delete;
+  virtual ~Request() noexcept = default;
+
+  /**
+   * Get the type of the request.
+   *
+   * A base request offers no functionality to promote it to the actual request
+   * type. By means of this function, the type of the underlying request as
+   * specified by Type is made available.
+   *
+   * @return The actual type of the request.
+   */
+  Type GetType() const noexcept { return m_uv_req->type; }
+
+  /**
+   * Get the name of the type of the request.  E.g. "connect" for connect.
+   */
+  const char* GetTypeName() const noexcept {
+    return uv_req_type_name(m_uv_req->type);
+  }
+
+  /**
+   * Cancel a pending request.
+   *
+   * This method fails if the request is executing or has finished
+   * executing.
+   * It can emit an error signal in case of errors.
+   *
+   * @return True in case of success, false otherwise.
+   */
+  bool Cancel() { return uv_cancel(m_uv_req) == 0; }
+
+  /**
+   * Return the size of the underlying request type.
+   * @return The size of the underlying request type.
+   */
+  size_t RawSize() const noexcept { return uv_req_size(m_uv_req->type); }
+
+  /**
+   * Get the underlying request data structure.
+   *
+   * @return The underlying request data structure.
+   */
+  uv_req_t* GetRawReq() noexcept { return m_uv_req; }
+
+  /**
+   * Get the underlying request data structure.
+   *
+   * @return The underlying request data structure.
+   */
+  const uv_req_t* GetRawReq() const noexcept { return m_uv_req; }
+
+  /**
+   * Keep this request in memory even if no outside shared_ptr references
+   * remain.  To release call Release().
+   *
+   * Derived classes can override this method for different memory management
+   * approaches (e.g. pooled storage of requests).
+   */
+  virtual void Keep() noexcept { m_self = shared_from_this(); }
+
+  /**
+   * No longer force holding this request in memory.  Does not immediately
+   * destroy the object unless no outside shared_ptr references remain.
+   *
+   * Derived classes can override this method for different memory management
+   * approaches (e.g. pooled storage of requests).
+   */
+  virtual void Release() noexcept { m_self.reset(); }
+
+  /**
+   * Error callback.  By default, this is set up to report errors to the handle
+   * that created this request.
+   * @param err error code
+   */
+  std::function<void(Error)> error;
+
+  /**
+   * Report an error.
+   * @param err Error code
+   */
+  void ReportError(int err) { error(Error(err)); }
+
+ protected:
+  /**
+   * Constructor.
+   */
+  explicit Request(uv_req_t* uv_req) : m_uv_req{uv_req} {
+    m_uv_req->data = this;
+  }
+
+ private:
+  std::shared_ptr<Request> m_self;
+  uv_req_t* m_uv_req;
+};
+
+/**
+ * Request.  Requests are not moveable or copyable.
+ * @tparam T CRTP derived class
+ * @tparam U underlying libuv request type
+ */
+template <typename T, typename U>
+class RequestImpl : public Request {
+ public:
+  std::shared_ptr<T> shared_from_this() {
+    return std::static_pointer_cast<T>(this->shared_from_this());
+  }
+
+  std::shared_ptr<const T> shared_from_this() const {
+    return std::static_pointer_cast<const T>(this->shared_from_this());
+  }
+
+  /**
+   * Get the underlying request data structure.
+   *
+   * @return The underlying request data structure.
+   */
+  U* GetRaw() noexcept { return &m_uv_req; }
+
+  /**
+   * Get the underlying request data structure.
+   *
+   * @return The underlying request data structure.
+   */
+  const U* GetRaw() const noexcept { return &m_uv_req; }
+
+ protected:
+  /**
+   * Constructor.
+   */
+  RequestImpl() : Request{reinterpret_cast<uv_req_t*>(&m_uv_req)} {}
+
+ private:
+  U m_uv_req;
+};
+
+}  // namespace wpi::uv
+
+#endif  // WPINET_UV_REQUEST_H_
diff --git a/wpinet/src/main/native/include/wpinet/uv/Signal.h b/wpinet/src/main/native/include/wpinet/uv/Signal.h
new file mode 100644
index 0000000..12b6839
--- /dev/null
+++ b/wpinet/src/main/native/include/wpinet/uv/Signal.h
@@ -0,0 +1,80 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#ifndef WPINET_UV_SIGNAL_H_
+#define WPINET_UV_SIGNAL_H_
+
+#include <uv.h>
+
+#include <memory>
+
+#include <wpi/Signal.h>
+
+#include "wpinet/uv/Handle.h"
+
+namespace wpi::uv {
+
+class Loop;
+
+/**
+ * Signal handle.
+ */
+class Signal final : public HandleImpl<Signal, uv_signal_t> {
+  struct private_init {};
+
+ public:
+  explicit Signal(const private_init&) {}
+  ~Signal() noexcept override = default;
+
+  /**
+   * Create a signal handle.
+   *
+   * @param loop Loop object where this handle runs.
+   */
+  static std::shared_ptr<Signal> Create(Loop& loop);
+
+  /**
+   * Create a signal handle.
+   *
+   * @param loop Loop object where this handle runs.
+   */
+  static std::shared_ptr<Signal> Create(const std::shared_ptr<Loop>& loop) {
+    return Create(*loop);
+  }
+
+  /**
+   * Start watching for the given signal.
+   *
+   * @param signum Signal to watch for.
+   */
+  void Start(int signum);
+
+  /**
+   * Start watching for the given signal.  Same as Start() but the signal
+   * handler is reset the moment the signal is received.
+   *
+   * @param signum Signal to watch for.
+   */
+  void StartOneshot(int signum);
+
+  /**
+   * Stop watching for the signal.
+   */
+  void Stop() { Invoke(&uv_signal_stop, GetRaw()); }
+
+  /**
+   * Get the signal being monitored.
+   * @return Signal number.
+   */
+  int GetSignal() const { return GetRaw()->signum; }
+
+  /**
+   * Signal generated when a signal occurs.
+   */
+  sig::Signal<int> signal;
+};
+
+}  // namespace wpi::uv
+
+#endif  // WPINET_UV_SIGNAL_H_
diff --git a/wpinet/src/main/native/include/wpinet/uv/Stream.h b/wpinet/src/main/native/include/wpinet/uv/Stream.h
new file mode 100644
index 0000000..9568455
--- /dev/null
+++ b/wpinet/src/main/native/include/wpinet/uv/Stream.h
@@ -0,0 +1,332 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#ifndef WPINET_UV_STREAM_H_
+#define WPINET_UV_STREAM_H_
+
+#include <uv.h>
+
+#include <cstdlib>
+#include <functional>
+#include <initializer_list>
+#include <memory>
+#include <span>
+#include <utility>
+
+#include <wpi/Signal.h>
+
+#include "wpinet/uv/Buffer.h"
+#include "wpinet/uv/Handle.h"
+#include "wpinet/uv/Request.h"
+
+namespace wpi::uv {
+
+class Stream;
+
+/**
+ * Shutdown request.
+ */
+class ShutdownReq : public RequestImpl<ShutdownReq, uv_shutdown_t> {
+ public:
+  ShutdownReq();
+
+  Stream& GetStream() const {
+    return *static_cast<Stream*>(GetRaw()->handle->data);
+  }
+
+  /**
+   * Shutdown completed signal.
+   */
+  sig::Signal<> complete;
+};
+
+/**
+ * Write request.
+ */
+class WriteReq : public RequestImpl<WriteReq, uv_write_t> {
+ public:
+  WriteReq();
+
+  Stream& GetStream() const {
+    return *static_cast<Stream*>(GetRaw()->handle->data);
+  }
+
+  /**
+   * Write completed signal.  This is called even if an error occurred.
+   * @param err error value
+   */
+  sig::Signal<Error> finish;
+};
+
+/**
+ * Stream handle.
+ * Stream handles provide an abstraction of a duplex communication channel.
+ * This is an abstract type; there are three stream implementations (Tcp,
+ * Pipe, and Tty).
+ */
+class Stream : public Handle {
+ public:
+  std::shared_ptr<Stream> shared_from_this() {
+    return std::static_pointer_cast<Stream>(Handle::shared_from_this());
+  }
+
+  std::shared_ptr<const Stream> shared_from_this() const {
+    return std::static_pointer_cast<const Stream>(Handle::shared_from_this());
+  }
+
+  /**
+   * Shutdown the outgoing (write) side of a duplex stream. It waits for pending
+   * write requests to complete.  HandleShutdownComplete() is called on the
+   * request after shutdown is complete.
+   *
+   * @param req shutdown request
+   */
+  void Shutdown(const std::shared_ptr<ShutdownReq>& req);
+
+  /**
+   * Shutdown the outgoing (write) side of a duplex stream. It waits for pending
+   * write requests to complete.  The callback is called after shutdown is
+   * complete.  Errors will be reported to the stream error handler.
+   *
+   * @param callback Callback function to call when shutdown completes
+   * @return Connection object for the callback
+   */
+  void Shutdown(std::function<void()> callback = nullptr);
+
+  /**
+   * Start reading data from an incoming stream.
+   *
+   * This will only succeed after a connection has been established.
+   *
+   * A data signal will be emitted several times until there is no more
+   * data to read or `StopRead()` is called.
+   * An end signal will be emitted when there is no more data to read.
+   */
+  void StartRead();
+
+  /**
+   * Stop reading data from the stream.
+   *
+   * This function is idempotent and may be safely called on a stopped stream.
+   */
+  void StopRead() { Invoke(&uv_read_stop, GetRawStream()); }
+
+  /**
+   * Write data to the stream.
+   *
+   * Data are written in order. The lifetime of the data pointers passed in
+   * the `bufs` parameter must exceed the lifetime of the write request.
+   * An easy way to ensure this is to have the write request keep track of
+   * the data and use either its Complete() function or destructor to free the
+   * data.
+   *
+   * The finish signal will be emitted on the request object when the data
+   * has been written (or if an error occurs).
+   * The error signal will be emitted on the request object in case of errors.
+   *
+   * @param bufs The buffers to be written to the stream.
+   * @param req write request
+   */
+  void Write(std::span<const Buffer> bufs,
+             const std::shared_ptr<WriteReq>& req);
+
+  /**
+   * Write data to the stream.
+   *
+   * Data are written in order. The lifetime of the data pointers passed in
+   * the `bufs` parameter must exceed the lifetime of the write request.
+   * An easy way to ensure this is to have the write request keep track of
+   * the data and use either its Complete() function or destructor to free the
+   * data.
+   *
+   * The finish signal will be emitted on the request object when the data
+   * has been written (or if an error occurs).
+   * The error signal will be emitted on the request object in case of errors.
+   *
+   * @param bufs The buffers to be written to the stream.
+   * @param req write request
+   */
+  void Write(std::initializer_list<Buffer> bufs,
+             const std::shared_ptr<WriteReq>& req) {
+    Write({bufs.begin(), bufs.end()}, req);
+  }
+
+  /**
+   * Write data to the stream.
+   *
+   * Data are written in order. The lifetime of the data pointers passed in
+   * the `bufs` parameter must exceed the lifetime of the write request.
+   * The callback can be used to free data after the request completes.
+   *
+   * The callback will be called when the data has been written (even if an
+   * error occurred).  Errors will be reported to the stream error handler.
+   *
+   * @param bufs The buffers to be written to the stream.
+   * @param callback Callback function to call when the write completes
+   */
+  void Write(std::span<const Buffer> bufs,
+             std::function<void(std::span<Buffer>, Error)> callback);
+
+  /**
+   * Write data to the stream.
+   *
+   * Data are written in order. The lifetime of the data pointers passed in
+   * the `bufs` parameter must exceed the lifetime of the write request.
+   * The callback can be used to free data after the request completes.
+   *
+   * The callback will be called when the data has been written (even if an
+   * error occurred).  Errors will be reported to the stream error handler.
+   *
+   * @param bufs The buffers to be written to the stream.
+   * @param callback Callback function to call when the write completes
+   */
+  void Write(std::initializer_list<Buffer> bufs,
+             std::function<void(std::span<Buffer>, Error)> callback) {
+    Write({bufs.begin(), bufs.end()}, std::move(callback));
+  }
+
+  /**
+   * Queue a write request if it can be completed immediately.
+   *
+   * Same as `Write()`, but won’t queue a write request if it can’t be
+   * completed immediately.
+   * An error signal will be emitted in case of errors.
+   *
+   * @param bufs The buffers to be written to the stream.
+   * @return Number of bytes written.
+   */
+  int TryWrite(std::span<const Buffer> bufs);
+
+  /**
+   * Queue a write request if it can be completed immediately.
+   *
+   * Same as `Write()`, but won’t queue a write request if it can’t be
+   * completed immediately.
+   * An error signal will be emitted in case of errors.
+   *
+   * @param bufs The buffers to be written to the stream.
+   * @return Number of bytes written.
+   */
+  int TryWrite(std::initializer_list<Buffer> bufs) {
+    return TryWrite({bufs.begin(), bufs.end()});
+  }
+
+  /**
+   * Same as TryWrite() and extended write function for sending handles over a
+   * pipe.
+   *
+   * Try to send a handle is not supported on Windows, where it returns
+   * UV_EAGAIN.
+   *
+   * @param bufs The buffers to be written to the stream.
+   * @param send send stream
+   * @return Number of bytes written.
+   */
+  int TryWrite2(std::span<const Buffer> bufs, Stream& send);
+
+  /**
+   * Same as TryWrite() and extended write function for sending handles over a
+   * pipe.
+   *
+   * Try to send a handle is not supported on Windows, where it returns
+   * UV_EAGAIN.
+   *
+   * @param bufs The buffers to be written to the stream.
+   * @param send send stream
+   * @return Number of bytes written.
+   */
+  int TryWrite2(std::initializer_list<Buffer> bufs, Stream& send) {
+    return TryWrite2({bufs.begin(), bufs.end()}, send);
+  }
+
+  /**
+   * Check if the stream is readable.
+   * @return True if the stream is readable, false otherwise.
+   */
+  bool IsReadable() const noexcept {
+    return uv_is_readable(GetRawStream()) == 1;
+  }
+
+  /**
+   * @brief Checks if the stream is writable.
+   * @return True if the stream is writable, false otherwise.
+   */
+  bool IsWritable() const noexcept {
+    return uv_is_writable(GetRawStream()) == 1;
+  }
+
+  /**
+   * Enable or disable blocking mode for a stream.
+   *
+   * When blocking mode is enabled all writes complete synchronously. The
+   * interface remains unchanged otherwise, e.g. completion or failure of the
+   * operation will still be reported through events which are emitted
+   * asynchronously.
+   *
+   * @param enable True to enable blocking mode, false otherwise.
+   * @return True in case of success, false otherwise.
+   */
+  bool SetBlocking(bool enable) noexcept {
+    return uv_stream_set_blocking(GetRawStream(), enable) == 0;
+  }
+
+  /**
+   * Gets the amount of queued bytes waiting to be sent.
+   * @return Amount of queued bytes waiting to be sent.
+   */
+  size_t GetWriteQueueSize() const noexcept {
+    return GetRawStream()->write_queue_size;
+  }
+
+  /**
+   * Get the underlying stream data structure.
+   *
+   * @return The underlying stream data structure.
+   */
+  uv_stream_t* GetRawStream() const noexcept {
+    return reinterpret_cast<uv_stream_t*>(GetRawHandle());
+  }
+
+  /**
+   * Signal generated when data was read on a stream.
+   */
+  sig::Signal<Buffer&, size_t> data;
+
+  /**
+   * Signal generated when no more read data is available.
+   */
+  sig::Signal<> end;
+
+ protected:
+  explicit Stream(uv_stream_t* uv_stream)
+      : Handle{reinterpret_cast<uv_handle_t*>(uv_stream)} {}
+};
+
+template <typename T, typename U>
+class StreamImpl : public Stream {
+ public:
+  std::shared_ptr<T> shared_from_this() {
+    return std::static_pointer_cast<T>(Handle::shared_from_this());
+  }
+
+  std::shared_ptr<const T> shared_from_this() const {
+    return std::static_pointer_cast<const T>(Handle::shared_from_this());
+  }
+
+  /**
+   * Get the underlying handle data structure.
+   *
+   * @return The underlying handle data structure.
+   */
+  U* GetRaw() const noexcept {
+    return reinterpret_cast<U*>(this->GetRawHandle());
+  }
+
+ protected:
+  StreamImpl() : Stream{static_cast<uv_stream_t*>(std::malloc(sizeof(U)))} {}
+};
+
+}  // namespace wpi::uv
+
+#endif  // WPINET_UV_STREAM_H_
diff --git a/wpinet/src/main/native/include/wpinet/uv/Tcp.h b/wpinet/src/main/native/include/wpinet/uv/Tcp.h
new file mode 100644
index 0000000..dc554db
--- /dev/null
+++ b/wpinet/src/main/native/include/wpinet/uv/Tcp.h
@@ -0,0 +1,371 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#ifndef WPINET_UV_TCP_H_
+#define WPINET_UV_TCP_H_
+
+#include <uv.h>
+
+#include <chrono>
+#include <functional>
+#include <memory>
+#include <string_view>
+#include <utility>
+
+#include "wpinet/uv/NetworkStream.h"
+
+namespace wpi::uv {
+
+class Loop;
+class TcpConnectReq;
+
+/**
+ * TCP handle.
+ * TCP handles are used to represent both TCP streams and servers.
+ */
+class Tcp final : public NetworkStreamImpl<Tcp, uv_tcp_t> {
+  struct private_init {};
+
+ public:
+  using Time = std::chrono::duration<uint64_t, std::milli>;
+
+  explicit Tcp(const private_init&) {}
+  ~Tcp() noexcept override = default;
+
+  /**
+   * Create a TCP handle.
+   *
+   * @param loop Loop object where this handle runs.
+   * @param flags Flags
+   */
+  static std::shared_ptr<Tcp> Create(Loop& loop,
+                                     unsigned int flags = AF_UNSPEC);
+
+  /**
+   * Create a TCP handle.
+   *
+   * @param loop Loop object where this handle runs.
+   * @param flags Flags
+   */
+  static std::shared_ptr<Tcp> Create(const std::shared_ptr<Loop>& loop,
+                                     unsigned int flags = AF_UNSPEC) {
+    return Create(*loop, flags);
+  }
+
+  /**
+   * Reuse this handle.  This closes the handle, and after the close completes,
+   * reinitializes it (identically to Create) and calls the provided callback.
+   * Unlike Close(), it does NOT emit the closed signal, however, IsClosing()
+   * will return true until the callback is called.  This does nothing if
+   * IsClosing() is true (e.g. if Close() was called).
+   *
+   * @param flags Flags
+   * @param callback Callback
+   */
+  void Reuse(std::function<void()> callback, unsigned int flags = AF_UNSPEC);
+
+  /**
+   * Accept incoming connection.
+   *
+   * This call is used in conjunction with `Listen()` to accept incoming
+   * connections. Call this function after receiving a ListenEvent event to
+   * accept the connection.
+   * An error signal will be emitted in case of errors.
+   *
+   * When the connection signal is emitted it is guaranteed that this
+   * function will complete successfully the first time. If you attempt to use
+   * it more than once, it may fail.
+   * It is suggested to only call this function once per connection signal.
+   *
+   * @return The stream handle for the accepted connection, or nullptr on error.
+   */
+  std::shared_ptr<Tcp> Accept();
+
+  /**
+   * Accept incoming connection.
+   *
+   * This call is used in conjunction with `Listen()` to accept incoming
+   * connections. Call this function after receiving a connection signal to
+   * accept the connection.
+   * An error signal will be emitted in case of errors.
+   *
+   * When the connection signal is emitted it is guaranteed that this
+   * function will complete successfully the first time. If you attempt to use
+   * it more than once, it may fail.
+   * It is suggested to only call this function once per connection signal.
+   *
+   * @param client Client stream object.
+   * @return False on error.
+   */
+  bool Accept(const std::shared_ptr<Tcp>& client) {
+    return NetworkStream::Accept(client);
+  }
+
+  /**
+   * Open an existing file descriptor or SOCKET as a TCP handle.
+   *
+   * @note The passed file descriptor or SOCKET is not checked for its type, but
+   * it's required that it represents a valid stream socket.
+   *
+   * @param sock A valid socket handle (either a file descriptor or a SOCKET).
+   */
+  void Open(uv_os_sock_t sock) { Invoke(&uv_tcp_open, GetRaw(), sock); }
+
+  /**
+   * Enable no delay operation (turns off Nagle's algorithm).
+   * @param enable True to enable it, false otherwise.
+   * @return True in case of success, false otherwise.
+   */
+  bool SetNoDelay(bool enable) { return uv_tcp_nodelay(GetRaw(), enable) == 0; }
+
+  /**
+   * Enable/Disable TCP keep-alive.
+   * @param enable True to enable it, false otherwise.
+   * @param time Initial delay in seconds (use
+   * `std::chrono::duration<unsigned int>`).
+   * @return True in case of success, false otherwise.
+   */
+  bool SetKeepAlive(bool enable, Time time = Time{0}) {
+    return uv_tcp_keepalive(GetRaw(), enable,
+                            static_cast<unsigned>(time.count())) == 0;
+  }
+
+  /**
+   * Enable/Disable simultaneous asynchronous accept requests.
+   *
+   * Enable/Disable simultaneous asynchronous accept requests that are
+   * queued by the operating system when listening for new TCP
+   * connections.
+   * This setting is used to tune a TCP server for the desired performance.
+   * Having simultaneous accepts can significantly improve the rate of
+   * accepting connections (which is why it is enabled by default) but may
+   * lead to uneven load distribution in multi-process setups.
+   *
+   * @param enable True to enable it, false otherwise.
+   * @return True in case of success, false otherwise.
+   */
+  bool SetSimultaneousAccepts(bool enable) {
+    return uv_tcp_simultaneous_accepts(GetRaw(), enable) == 0;
+  }
+
+  /**
+   * Bind the handle to an IPv4 or IPv6 address and port.
+   *
+   * A successful call to this function does not guarantee that the call to
+   * `Listen()` or `Connect()` will work properly.
+   * An error signal can be emitted because of either this function or the
+   * ones mentioned above.
+   *
+   * @param addr Initialized `sockaddr_in` or `sockaddr_in6` data structure.
+   * @param flags Optional additional flags.
+   */
+  void Bind(const sockaddr& addr, unsigned int flags = 0) {
+    Invoke(&uv_tcp_bind, GetRaw(), &addr, flags);
+  }
+
+  void Bind(const sockaddr_in& addr, unsigned int flags = 0) {
+    Bind(reinterpret_cast<const sockaddr&>(addr), flags);
+  }
+
+  void Bind(const sockaddr_in6& addr, unsigned int flags = 0) {
+    Bind(reinterpret_cast<const sockaddr&>(addr), flags);
+  }
+
+  /**
+   * Bind the handle to an IPv4 address and port.
+   *
+   * A successful call to this function does not guarantee that the call to
+   * `Listen()` or `Connect()` will work properly.
+   * An error signal can be emitted because of either this function or the
+   * ones mentioned above.
+   *
+   * Available flags are:
+   *
+   * @param ip The address to which to bind.
+   * @param port The port to which to bind.
+   * @param flags Optional additional flags.
+   */
+  void Bind(std::string_view ip, unsigned int port, unsigned int flags = 0);
+
+  /**
+   * Bind the handle to an IPv6 address and port.
+   *
+   * A successful call to this function does not guarantee that the call to
+   * `Listen()` or `Connect()` will work properly.
+   * An error signal can be emitted because of either this function or the
+   * ones mentioned above.
+   *
+   * Available flags are:
+   *
+   * @param ip The address to which to bind.
+   * @param port The port to which to bind.
+   * @param flags Optional additional flags.
+   */
+  void Bind6(std::string_view ip, unsigned int port, unsigned int flags = 0);
+
+  /**
+   * Get the current address to which the handle is bound.
+   * @return The address (will be zeroed if an error occurred).
+   */
+  sockaddr_storage GetSock();
+
+  /**
+   * Get the address of the peer connected to the handle.
+   * @return The address (will be zeroed if an error occurred).
+   */
+  sockaddr_storage GetPeer();
+
+  /**
+   * Establish an IPv4 or IPv6 TCP connection.
+   *
+   * On Windows if the addr is initialized to point to an unspecified address
+   * (`0.0.0.0` or `::`) it will be changed to point to localhost. This is
+   * done to match the behavior of Linux systems.
+   *
+   * The connected signal is emitted on the request when the connection has been
+   * established.
+   * The error signal is emitted on the request in case of errors during the
+   * connection.
+   *
+   * @param addr Initialized `sockaddr_in` or `sockaddr_in6` data structure.
+   * @param req connection request
+   */
+  void Connect(const sockaddr& addr, const std::shared_ptr<TcpConnectReq>& req);
+
+  void Connect(const sockaddr_in& addr,
+               const std::shared_ptr<TcpConnectReq>& req) {
+    Connect(reinterpret_cast<const sockaddr&>(addr), req);
+  }
+
+  void Connect(const sockaddr_in6& addr,
+               const std::shared_ptr<TcpConnectReq>& req) {
+    Connect(reinterpret_cast<const sockaddr&>(addr), req);
+  }
+
+  /**
+   * Establish an IPv4 or IPv6 TCP connection.
+   *
+   * On Windows if the addr is initialized to point to an unspecified address
+   * (`0.0.0.0` or `::`) it will be changed to point to localhost. This is
+   * done to match the behavior of Linux systems.
+   *
+   * The callback is called when the connection has been established.  Errors
+   * are reported to the stream error handler.
+   *
+   * @param addr Initialized `sockaddr_in` or `sockaddr_in6` data structure.
+   * @param callback Callback function to call when connection established
+   */
+  void Connect(const sockaddr& addr, std::function<void()> callback);
+
+  void Connect(const sockaddr_in& addr, std::function<void()> callback) {
+    Connect(reinterpret_cast<const sockaddr&>(addr), std::move(callback));
+  }
+
+  void Connect(const sockaddr_in6& addr, std::function<void()> callback) {
+    Connect(reinterpret_cast<const sockaddr&>(addr), std::move(callback));
+  }
+
+  /**
+   * Establish an IPv4 TCP connection.
+   *
+   * On Windows if the addr is initialized to point to an unspecified address
+   * (`0.0.0.0` or `::`) it will be changed to point to localhost. This is
+   * done to match the behavior of Linux systems.
+   *
+   * The connected signal is emitted on the request when the connection has been
+   * established.
+   * The error signal is emitted on the request in case of errors during the
+   * connection.
+   *
+   * @param ip The address to which to connect to.
+   * @param port The port to which to connect to.
+   * @param req connection request
+   */
+  void Connect(std::string_view ip, unsigned int port,
+               const std::shared_ptr<TcpConnectReq>& req);
+
+  /**
+   * Establish an IPv4 TCP connection.
+   *
+   * On Windows if the addr is initialized to point to an unspecified address
+   * (`0.0.0.0` or `::`) it will be changed to point to localhost. This is
+   * done to match the behavior of Linux systems.
+   *
+   * The callback is called when the connection has been established.  Errors
+   * are reported to the stream error handler.
+   *
+   * @param ip The address to which to connect to.
+   * @param port The port to which to connect to.
+   * @param callback Callback function to call when connection established
+   */
+  void Connect(std::string_view ip, unsigned int port,
+               std::function<void()> callback);
+
+  /**
+   * Establish an IPv6 TCP connection.
+   *
+   * On Windows if the addr is initialized to point to an unspecified address
+   * (`0.0.0.0` or `::`) it will be changed to point to localhost. This is
+   * done to match the behavior of Linux systems.
+   *
+   * The connected signal is emitted on the request when the connection has been
+   * established.
+   * The error signal is emitted on the request in case of errors during the
+   * connection.
+   *
+   * @param ip The address to which to connect to.
+   * @param port The port to which to connect to.
+   * @param req connection request
+   */
+  void Connect6(std::string_view ip, unsigned int port,
+                const std::shared_ptr<TcpConnectReq>& req);
+
+  /**
+   * Establish an IPv6 TCP connection.
+   *
+   * On Windows if the addr is initialized to point to an unspecified address
+   * (`0.0.0.0` or `::`) it will be changed to point to localhost. This is
+   * done to match the behavior of Linux systems.
+   *
+   * The callback is called when the connection has been established.  Errors
+   * are reported to the stream error handler.
+   *
+   * @param ip The address to which to connect to.
+   * @param port The port to which to connect to.
+   * @param callback Callback function to call when connection established
+   */
+  void Connect6(std::string_view ip, unsigned int port,
+                std::function<void()> callback);
+
+  /**
+   * Resets a TCP connection by sending a RST packet. This is accomplished by
+   * setting the SO_LINGER socket option with a linger interval of zero and then
+   * calling Close(). Due to some platform inconsistencies, mixing of
+   * Shutdown() and CloseReset() calls is not allowed.
+   */
+  void CloseReset();
+
+ private:
+  Tcp* DoAccept() override;
+
+  struct ReuseData {
+    std::function<void()> callback;
+    unsigned int flags;
+  };
+  std::unique_ptr<ReuseData> m_reuseData;
+};
+
+/**
+ * TCP connection request.
+ */
+class TcpConnectReq : public ConnectReq {
+ public:
+  Tcp& GetStream() const {
+    return *static_cast<Tcp*>(&ConnectReq::GetStream());
+  }
+};
+
+}  // namespace wpi::uv
+
+#endif  // WPINET_UV_TCP_H_
diff --git a/wpinet/src/main/native/include/wpinet/uv/Timer.h b/wpinet/src/main/native/include/wpinet/uv/Timer.h
new file mode 100644
index 0000000..4a1cf54
--- /dev/null
+++ b/wpinet/src/main/native/include/wpinet/uv/Timer.h
@@ -0,0 +1,145 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#ifndef WPINET_UV_TIMER_H_
+#define WPINET_UV_TIMER_H_
+
+#include <uv.h>
+
+#include <chrono>
+#include <functional>
+#include <memory>
+#include <utility>
+
+#include <wpi/Signal.h>
+
+#include "wpinet/uv/Handle.h"
+
+namespace wpi::uv {
+
+class Loop;
+
+/**
+ * Timer handle.
+ * Timer handles are used to schedule signals to be called in the future.
+ */
+class Timer final : public HandleImpl<Timer, uv_timer_t> {
+  struct private_init {};
+
+ public:
+  using Time = std::chrono::duration<uint64_t, std::milli>;
+
+  explicit Timer(const private_init&) {}
+  ~Timer() noexcept override = default;
+
+  /**
+   * Create a timer handle.
+   *
+   * @param loop Loop object where this handle runs.
+   */
+  static std::shared_ptr<Timer> Create(Loop& loop);
+
+  /**
+   * Create a timer handle.
+   *
+   * @param loop Loop object where this handle runs.
+   */
+  static std::shared_ptr<Timer> Create(const std::shared_ptr<Loop>& loop) {
+    return Create(*loop);
+  }
+
+  /**
+   * Create a timer that calls a functor after a given time interval.
+   *
+   * @param loop Loop object where the timer should run.
+   * @param timeout Time interval
+   * @param func Functor
+   */
+  static void SingleShot(Loop& loop, Time timeout, std::function<void()> func);
+
+  /**
+   * Create a timer that calls a functor after a given time interval.
+   *
+   * @param loop Loop object where the timer should run.
+   * @param timeout Time interval
+   * @param func Functor
+   */
+  static void SingleShot(const std::shared_ptr<Loop>& loop, Time timeout,
+                         std::function<void()> func) {
+    return SingleShot(*loop, timeout, std::move(func));
+  }
+
+  /**
+   * Start the timer.
+   *
+   * If timeout is zero, an event is emitted on the next event loop
+   * iteration. If repeat is non-zero, an event is emitted first
+   * after timeout milliseconds and then repeatedly after repeat milliseconds.
+   *
+   * @param timeout Milliseconds before to emit an event (use
+   * `std::chrono::duration<uint64_t, std::milli>`).
+   * @param repeat Milliseconds between successive events (use
+   * `std::chrono::duration<uint64_t, std::milli>`).
+   */
+  void Start(Time timeout, Time repeat = Time{0});
+
+  /**
+   * Stop the timer.
+   */
+  void Stop() { Invoke(&uv_timer_stop, GetRaw()); }
+
+  /**
+   * Stop the timer and restart it if it was repeating.
+   *
+   * Stop the timer, and if it is repeating restart it using the repeat value
+   * as the timeout.
+   * If the timer has never been started before it emits sigError.
+   */
+  void Again() { Invoke(&uv_timer_again, GetRaw()); }
+
+  /**
+   * Set the repeat interval value.
+   *
+   * The timer will be scheduled to run on the given interval and will follow
+   * normal timer semantics in the case of a time-slice overrun.
+   * For example, if a 50ms repeating timer first runs for 17ms, it will be
+   * scheduled to run again 33ms later. If other tasks consume more than the
+   * 33ms following the first timer event, then another event will be emitted
+   * as soon as possible.
+   *
+   * If the repeat value is set from a listener bound to an event, it does
+   * not immediately take effect. If the timer was non-repeating before, it
+   * will have been stopped. If it was repeating, then the old repeat value
+   * will have been used to schedule the next timeout.
+   *
+   * @param repeat Repeat interval in milliseconds (use
+   * `std::chrono::duration<uint64_t, std::milli>`).
+   */
+  void SetRepeat(Time repeat) { uv_timer_set_repeat(GetRaw(), repeat.count()); }
+
+  /**
+   * Get the timer repeat value.
+   * @return Timer repeat value in milliseconds (as a
+   * `std::chrono::duration<uint64_t, std::milli>`).
+   */
+  Time GetRepeat() const { return Time{uv_timer_get_repeat(GetRaw())}; }
+
+  /**
+   * Get the timer due value or 0 if it has expired. The time is relative to
+   * uv_now().
+   *
+   * @return Timer due value in milliseconds (as a
+   * `std::chrono::duration<uint64_t, std::milli>`).
+   */
+  Time GetDueIn() const { return Time{uv_timer_get_due_in(GetRaw())}; }
+
+  /**
+   * Signal generated when the timeout event occurs.
+   */
+  sig::Signal<> timeout;
+};
+
+}  // namespace wpi::uv
+
+#endif  // WPINET_UV_TIMER_H_
diff --git a/wpinet/src/main/native/include/wpinet/uv/Tty.h b/wpinet/src/main/native/include/wpinet/uv/Tty.h
new file mode 100644
index 0000000..2dd4f97
--- /dev/null
+++ b/wpinet/src/main/native/include/wpinet/uv/Tty.h
@@ -0,0 +1,85 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#ifndef WPINET_UV_TTY_H_
+#define WPINET_UV_TTY_H_
+
+#include <uv.h>
+
+#include <memory>
+#include <utility>
+
+#include "wpinet/uv/Stream.h"
+
+namespace wpi::uv {
+
+class Loop;
+class Tty;
+
+/**
+ * TTY handle.
+ * TTY handles represent a stream for the console.
+ */
+class Tty final : public StreamImpl<Tty, uv_tty_t> {
+  struct private_init {};
+
+ public:
+  explicit Tty(const private_init&) {}
+  ~Tty() noexcept override = default;
+
+  /**
+   * Create a TTY handle.
+   *
+   * @param loop Loop object where this handle runs.
+   * @param fd File descriptor, usually 0=stdin, 1=stdout, 2=stderr
+   * @param readable Specifies if you plan on calling StartRead().  stdin is
+   *                 readable, stdout is not.
+   */
+  static std::shared_ptr<Tty> Create(Loop& loop, uv_file fd, bool readable);
+
+  /**
+   * Create a TTY handle.
+   *
+   * @param loop Loop object where this handle runs.
+   * @param fd File descriptor, usually 0=stdin, 1=stdout, 2=stderr
+   * @param readable Specifies if you plan on calling StartRead().  stdin is
+   *                 readable, stdout is not.
+   */
+  static std::shared_ptr<Tty> Create(const std::shared_ptr<Loop>& loop,
+                                     uv_file fd, bool readable) {
+    return Create(*loop, fd, readable);
+  }
+
+  /**
+   * Set the TTY using the specified terminal mode.
+   *
+   * @param mode terminal mode
+   */
+  void SetMode(uv_tty_mode_t mode) {
+    int err = uv_tty_set_mode(GetRaw(), mode);
+    if (err < 0) {
+      ReportError(err);
+    }
+  }
+
+  /**
+   * Reset TTY settings to default values for the next process to take over.
+   * Typically called when the program exits.
+   */
+  void ResetMode() { Invoke(&uv_tty_reset_mode); }
+
+  /**
+   * Gets the current window size.
+   * @return Window size (pair of width and height).
+   */
+  std::pair<int, int> GetWindowSize() {
+    int width = 0, height = 0;
+    Invoke(&uv_tty_get_winsize, GetRaw(), &width, &height);
+    return std::make_pair(width, height);
+  }
+};
+
+}  // namespace wpi::uv
+
+#endif  // WPINET_UV_TTY_H_
diff --git a/wpinet/src/main/native/include/wpinet/uv/Udp.h b/wpinet/src/main/native/include/wpinet/uv/Udp.h
new file mode 100644
index 0000000..cfa245f
--- /dev/null
+++ b/wpinet/src/main/native/include/wpinet/uv/Udp.h
@@ -0,0 +1,400 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#ifndef WPINET_UV_UDP_H_
+#define WPINET_UV_UDP_H_
+
+#include <uv.h>
+
+#include <functional>
+#include <memory>
+#include <span>
+#include <string_view>
+#include <utility>
+
+#include <wpi/Signal.h>
+
+#include "wpinet/uv/Handle.h"
+#include "wpinet/uv/Request.h"
+
+namespace wpi::uv {
+
+class Loop;
+class Udp;
+
+/**
+ * UDP send request.
+ */
+class UdpSendReq : public RequestImpl<UdpSendReq, uv_udp_send_t> {
+ public:
+  UdpSendReq();
+
+  Udp& GetUdp() const { return *static_cast<Udp*>(GetRaw()->handle->data); }
+
+  /**
+   * Send completed signal.  This is called even if an error occurred.
+   * @param err error value
+   */
+  sig::Signal<Error> complete;
+};
+
+/**
+ * UDP handle.
+ * UDP handles encapsulate UDP communication for both clients and servers.
+ */
+class Udp final : public HandleImpl<Udp, uv_udp_t> {
+  struct private_init {};
+
+ public:
+  explicit Udp(const private_init&) {}
+  ~Udp() noexcept override = default;
+
+  /**
+   * Create a UDP handle.
+   *
+   * @param loop Loop object where this handle runs.
+   * @param flags Flags
+   */
+  static std::shared_ptr<Udp> Create(Loop& loop,
+                                     unsigned int flags = AF_UNSPEC);
+
+  /**
+   * Create a UDP handle.
+   *
+   * @param loop Loop object where this handle runs.
+   * @param flags Flags
+   */
+  static std::shared_ptr<Udp> Create(const std::shared_ptr<Loop>& loop,
+                                     unsigned int flags = AF_UNSPEC) {
+    return Create(*loop, flags);
+  }
+
+  /**
+   * Open an existing file descriptor or SOCKET as a UDP handle.
+   *
+   * @param sock A valid socket handle (either a file descriptor or a SOCKET).
+   */
+  void Open(uv_os_sock_t sock) { Invoke(&uv_udp_open, GetRaw(), sock); }
+
+  /**
+   * Bind the handle to an IPv4 or IPv6 address and port.
+   *
+   * @param addr Initialized `sockaddr_in` or `sockaddr_in6` data structure.
+   * @param flags Optional additional flags.
+   */
+  void Bind(const sockaddr& addr, unsigned int flags = 0) {
+    Invoke(&uv_udp_bind, GetRaw(), &addr, flags);
+  }
+
+  void Bind(const sockaddr_in& addr, unsigned int flags = 0) {
+    Bind(reinterpret_cast<const sockaddr&>(addr), flags);
+  }
+
+  void Bind(const sockaddr_in6& addr, unsigned int flags = 0) {
+    Bind(reinterpret_cast<const sockaddr&>(addr), flags);
+  }
+
+  /**
+   * Bind the handle to an IPv4 address and port.
+   *
+   * @param ip The address to which to bind.
+   * @param port The port to which to bind.
+   * @param flags Optional additional flags.
+   */
+  void Bind(std::string_view ip, unsigned int port, unsigned int flags = 0);
+
+  /**
+   * Bind the handle to an IPv6 address and port.
+   *
+   * @param ip The address to which to bind.
+   * @param port The port to which to bind.
+   * @param flags Optional additional flags.
+   */
+  void Bind6(std::string_view ip, unsigned int port, unsigned int flags = 0);
+
+  /**
+   * Associate the handle to a remote address and port, so every message sent
+   * by this handle is automatically sent to that destination.
+   *
+   * @param addr Initialized `sockaddr_in` or `sockaddr_in6` data structure.
+   */
+  void Connect(const sockaddr& addr) {
+    Invoke(&uv_udp_connect, GetRaw(), &addr);
+  }
+
+  void Connect(const sockaddr_in& addr) {
+    Connect(reinterpret_cast<const sockaddr&>(addr));
+  }
+
+  void Connect(const sockaddr_in6& addr) {
+    Connect(reinterpret_cast<const sockaddr&>(addr));
+  }
+
+  /**
+   * Associate the handle to an IPv4 address and port, so every message sent
+   * by this handle is automatically sent to that destination.
+   *
+   * @param ip The address to which to bind.
+   * @param port The port to which to bind.
+   */
+  void Connect(std::string_view ip, unsigned int port);
+
+  /**
+   * Associate the handle to an IPv6 address and port, so every message sent
+   * by this handle is automatically sent to that destination.
+   *
+   * @param ip The address to which to bind.
+   * @param port The port to which to bind.
+   * @param flags Optional additional flags.
+   */
+  void Connect6(std::string_view ip, unsigned int port);
+
+  /**
+   * Get the remote IP and port on connected UDP handles.
+   * @return The address (will be zeroed if an error occurred).
+   */
+  sockaddr_storage GetPeer();
+
+  /**
+   * Get the current address to which the handle is bound.
+   * @return The address (will be zeroed if an error occurred).
+   */
+  sockaddr_storage GetSock();
+
+  /**
+   * Set membership for a multicast address.
+   *
+   * @param multicastAddr Multicast address to set membership for
+   * @param interfaceAddr Interface address
+   * @param membership Should be UV_JOIN_GROUP or UV_LEAVE_GROUP
+   */
+  void SetMembership(std::string_view multicastAddr,
+                     std::string_view interfaceAddr, uv_membership membership);
+
+  /**
+   * Set membership for a source-specific multicast group.
+   *
+   * @param multicastAddr Multicast address to set membership for
+   * @param interfaceAddr Interface address
+   * @param sourceAddr Source address
+   * @param membership Should be UV_JOIN_GROUP or UV_LEAVE_GROUP
+   */
+  void SetSourceMembership(std::string_view multicastAddr,
+                           std::string_view interfaceAddr,
+                           std::string_view sourceAddr,
+                           uv_membership membership);
+
+  /**
+   * Set IP multicast loop flag.  Makes multicast packets loop back to local
+   * sockets.
+   *
+   * @param enabled True for enabled, false for disabled
+   */
+  void SetMulticastLoop(bool enabled) {
+    Invoke(&uv_udp_set_multicast_loop, GetRaw(), enabled ? 1 : 0);
+  }
+
+  /**
+   * Set the multicast TTL.
+   *
+   * @param ttl Time to live (1-255)
+   */
+  void SetMulticastTtl(int ttl) {
+    Invoke(&uv_udp_set_multicast_ttl, GetRaw(), ttl);
+  }
+
+  /**
+   * Set the multicast interface to send or receive data on.
+   *
+   * @param interfaceAddr Interface address
+   */
+  void SetMulticastInterface(std::string_view interfaceAddr);
+
+  /**
+   * Set broadcast on or off.
+   *
+   * @param enabled True for enabled, false for disabled
+   */
+  void SetBroadcast(bool enabled) {
+    Invoke(&uv_udp_set_broadcast, GetRaw(), enabled ? 1 : 0);
+  }
+
+  /**
+   * Set the time to live (TTL).
+   *
+   * @param ttl Time to live (1-255)
+   */
+  void SetTtl(int ttl) { Invoke(&uv_udp_set_ttl, GetRaw(), ttl); }
+
+  /**
+   * Send data over the UDP socket.  If the socket has not previously been bound
+   * with Bind() it will be bound to 0.0.0.0 (the "all interfaces" IPv4 address)
+   * and a random port number.
+   *
+   * Data are written in order. The lifetime of the data pointers passed in
+   * the `bufs` parameter must exceed the lifetime of the send request.
+   * The callback can be used to free data after the request completes.
+   *
+   * HandleSendComplete() will be called on the request object when the data
+   * has been written.  HandleSendComplete() is called even if an error occurs.
+   * HandleError() will be called on the request object in case of errors.
+   *
+   * @param addr sockaddr_in or sockaddr_in6 with the address and port of the
+   *             remote peer.
+   * @param bufs The buffers to be written to the stream.
+   * @param req write request
+   */
+  void Send(const sockaddr& addr, std::span<const Buffer> bufs,
+            const std::shared_ptr<UdpSendReq>& req);
+
+  void Send(const sockaddr_in& addr, std::span<const Buffer> bufs,
+            const std::shared_ptr<UdpSendReq>& req) {
+    Send(reinterpret_cast<const sockaddr&>(addr), bufs, req);
+  }
+
+  void Send(const sockaddr_in6& addr, std::span<const Buffer> bufs,
+            const std::shared_ptr<UdpSendReq>& req) {
+    Send(reinterpret_cast<const sockaddr&>(addr), bufs, req);
+  }
+
+  /**
+   * Variant of Send() for connected sockets.  Cannot be used with
+   * connectionless sockets.
+   *
+   * @param bufs The buffers to be written to the stream.
+   * @param req write request
+   */
+  void Send(std::span<const Buffer> bufs,
+            const std::shared_ptr<UdpSendReq>& req);
+
+  /**
+   * Send data over the UDP socket.  If the socket has not previously been bound
+   * with Bind() it will be bound to 0.0.0.0 (the "all interfaces" IPv4 address)
+   * and a random port number.
+   *
+   * Data are written in order. The lifetime of the data pointers passed in
+   * the `bufs` parameter must exceed the lifetime of the send request.
+   * The callback can be used to free data after the request completes.
+   *
+   * The callback will be called when the data has been sent.  Errors will
+   * be reported via the error signal.
+   *
+   * @param addr sockaddr_in or sockaddr_in6 with the address and port of the
+   *             remote peer.
+   * @param bufs The buffers to be sent.
+   * @param callback Callback function to call when the data has been sent.
+   */
+  void Send(const sockaddr& addr, std::span<const Buffer> bufs,
+            std::function<void(std::span<Buffer>, Error)> callback);
+
+  void Send(const sockaddr_in& addr, std::span<const Buffer> bufs,
+            std::function<void(std::span<Buffer>, Error)> callback) {
+    Send(reinterpret_cast<const sockaddr&>(addr), bufs, std::move(callback));
+  }
+
+  void Send(const sockaddr_in6& addr, std::span<const Buffer> bufs,
+            std::function<void(std::span<Buffer>, Error)> callback) {
+    Send(reinterpret_cast<const sockaddr&>(addr), bufs, std::move(callback));
+  }
+
+  /**
+   * Variant of Send() for connected sockets.  Cannot be used with
+   * connectionless sockets.
+   *
+   * @param bufs The buffers to be written to the stream.
+   * @param callback Callback function to call when the data has been sent.
+   */
+  void Send(std::span<const Buffer> bufs,
+            std::function<void(std::span<Buffer>, Error)> callback);
+
+  /**
+   * Same as Send(), but won't queue a send request if it can't be completed
+   * immediately.
+   *
+   * @param addr sockaddr_in or sockaddr_in6 with the address and port of the
+   *             remote peer.
+   * @param bufs The buffers to be send.
+   * @return Number of bytes sent.
+   */
+  int TrySend(const sockaddr& addr, std::span<const Buffer> bufs) {
+    int val = uv_udp_try_send(GetRaw(), bufs.data(),
+                              static_cast<unsigned>(bufs.size()), &addr);
+    if (val < 0) {
+      this->ReportError(val);
+      return 0;
+    }
+    return val;
+  }
+
+  int TrySend(const sockaddr_in& addr, std::span<const Buffer> bufs) {
+    return TrySend(reinterpret_cast<const sockaddr&>(addr), bufs);
+  }
+
+  int TrySend(const sockaddr_in6& addr, std::span<const Buffer> bufs) {
+    return TrySend(reinterpret_cast<const sockaddr&>(addr), bufs);
+  }
+
+  /**
+   * Variant of TrySend() for connected sockets.  Cannot be used with
+   * connectionless sockets.
+   *
+   * @param bufs The buffers to be written to the stream.
+   * @return Number of bytes sent.
+   */
+  int TrySend(std::span<const Buffer> bufs) {
+    int val = uv_udp_try_send(GetRaw(), bufs.data(),
+                              static_cast<unsigned>(bufs.size()), nullptr);
+    if (val < 0) {
+      this->ReportError(val);
+      return 0;
+    }
+    return val;
+  }
+
+  /**
+   * Prepare for receiving data.  If the socket has not previously been bound
+   * with Bind() it is bound to 0.0.0.0 (the "all interfaces" IPv4 address) and
+   * a random port number.
+   *
+   * A received signal will be emitted for each received data packet until
+   * `StopRecv()` is called.
+   */
+  void StartRecv();
+
+  /**
+   * Stop listening for incoming datagrams.
+   */
+  void StopRecv() { Invoke(&uv_udp_recv_stop, GetRaw()); }
+
+  /**
+   * Returns true if the UDP handle was created with the UV_UDP_RECVMMSG flag
+   * and the platform supports recvmmsg(2), false otherwise.
+   * @return True if the UDP handle is using recvmmsg.
+   */
+  bool IsUsingRecvmmsg() const { return uv_udp_using_recvmmsg(GetRaw()); }
+
+  /**
+   * Gets the amount of queued bytes waiting to be sent.
+   * @return Amount of queued bytes waiting to be sent.
+   */
+  size_t GetSendQueueSize() const noexcept { return GetRaw()->send_queue_size; }
+
+  /**
+   * Gets the amount of queued packets waiting to be sent.
+   * @return Amount of queued packets waiting to be sent.
+   */
+  size_t GetSendQueueCount() const noexcept {
+    return GetRaw()->send_queue_count;
+  }
+
+  /**
+   * Signal generated for each incoming datagram.  Parameters are the buffer,
+   * the number of bytes received, the address of the sender, and flags.
+   */
+  sig::Signal<Buffer&, size_t, const sockaddr&, unsigned> received;
+};
+
+}  // namespace wpi::uv
+
+#endif  // WPINET_UV_UDP_H_
diff --git a/wpinet/src/main/native/include/wpinet/uv/Work.h b/wpinet/src/main/native/include/wpinet/uv/Work.h
new file mode 100644
index 0000000..72293d0
--- /dev/null
+++ b/wpinet/src/main/native/include/wpinet/uv/Work.h
@@ -0,0 +1,94 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#ifndef WPINET_UV_WORK_H_
+#define WPINET_UV_WORK_H_
+
+#include <uv.h>
+
+#include <functional>
+#include <memory>
+#include <utility>
+
+#include <wpi/Signal.h>
+
+#include "wpinet/uv/Request.h"
+
+namespace wpi::uv {
+
+class Loop;
+
+/**
+ * Work request.
+ * For use with `QueueWork()` function family.
+ */
+class WorkReq : public RequestImpl<WorkReq, uv_work_t> {
+ public:
+  WorkReq();
+
+  Loop& GetLoop() const { return *static_cast<Loop*>(GetRaw()->loop->data); }
+
+  /**
+   * Function(s) that will be run on the thread pool.
+   */
+  sig::Signal<> work;
+
+  /**
+   * Function(s) that will be run on the loop thread after the work on the
+   * thread pool has been completed by the work callback.
+   */
+  sig::Signal<> afterWork;
+};
+
+/**
+ * Initializes a work request which will run on the thread pool.
+ *
+ * @param loop Event loop
+ * @param req request
+ */
+void QueueWork(Loop& loop, const std::shared_ptr<WorkReq>& req);
+
+/**
+ * Initializes a work request which will run on the thread pool.
+ *
+ * @param loop Event loop
+ * @param req request
+ */
+inline void QueueWork(const std::shared_ptr<Loop>& loop,
+                      const std::shared_ptr<WorkReq>& req) {
+  QueueWork(*loop, req);
+}
+
+/**
+ * Initializes a work request which will run on the thread pool.  The work
+ * callback will be run on a thread from the thread pool, and the afterWork
+ * callback will be called on the loop thread after the work completes.  Any
+ * errors are forwarded to the loop.
+ *
+ * @param loop Event loop
+ * @param work Work callback (called from separate thread)
+ * @param afterWork After work callback (called on loop thread)
+ */
+void QueueWork(Loop& loop, std::function<void()> work,
+               std::function<void()> afterWork);
+
+/**
+ * Initializes a work request which will run on the thread pool.  The work
+ * callback will be run on a thread from the thread pool, and the afterWork
+ * callback will be called on the loop thread after the work completes.  Any
+ * errors are forwarded to the loop.
+ *
+ * @param loop Event loop
+ * @param work Work callback (called from separate thread)
+ * @param afterWork After work callback (called on loop thread)
+ */
+inline void QueueWork(const std::shared_ptr<Loop>& loop,
+                      std::function<void()> work,
+                      std::function<void()> afterWork) {
+  QueueWork(*loop, std::move(work), std::move(afterWork));
+}
+
+}  // namespace wpi::uv
+
+#endif  // WPINET_UV_WORK_H_
diff --git a/wpinet/src/main/native/include/wpinet/uv/util.h b/wpinet/src/main/native/include/wpinet/uv/util.h
new file mode 100644
index 0000000..1967c7a
--- /dev/null
+++ b/wpinet/src/main/native/include/wpinet/uv/util.h
@@ -0,0 +1,151 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#ifndef WPINET_UV_UTIL_H_
+#define WPINET_UV_UTIL_H_
+
+#include <uv.h>
+
+#include <cstring>
+#include <string_view>
+
+#ifdef _WIN32
+#pragma comment(lib, "Ws2_32.lib")
+#endif
+
+namespace wpi::uv {
+
+/**
+ * Convert a binary structure containing an IPv4 address to a string.
+ * @param addr Binary structure
+ * @param ip Output string (any type that has `assign(char*, char*)`)
+ * @param port Output port number
+ * @return Error (same as `uv_ip4_name()`).
+ */
+template <typename T>
+int AddrToName(const sockaddr_in& addr, T* ip, unsigned int* port) {
+  char name[128];
+  int err = uv_ip4_name(&addr, name, 128);
+  if (err == 0) {
+    ip->assign(name, name + std::strlen(name));
+    *port = ntohs(addr.sin_port);
+  } else {
+    ip->assign(name, name);
+  }
+  return err;
+}
+
+/**
+ * Convert a binary structure containing an IPv6 address to a string.
+ * @param addr Binary structure
+ * @param ip Output string (any type that has `assign(char*, char*)`)
+ * @param port Output port number
+ * @return Error (same as `uv_ip6_name()`).
+ */
+template <typename T>
+int AddrToName(const sockaddr_in6& addr, T* ip, unsigned int* port) {
+  char name[128];
+  int err = uv_ip6_name(&addr, name, 128);
+  if (err == 0) {
+    ip->assign(name, name + std::strlen(name));
+    *port = ntohs(addr.sin6_port);
+  } else {
+    ip->assign(name, name);
+  }
+  return err;
+}
+
+/**
+ * Convert a binary structure containing an IPv4 or IPv6 address to a string.
+ * @param addr Binary structure
+ * @param ip Output string (any type that has `assign(char*, char*)`)
+ * @param port Output port number
+ * @return Error (same as `uv_ip6_name()`).
+ */
+template <typename T>
+int AddrToName(const sockaddr_storage& addr, T* ip, unsigned int* port) {
+  if (addr.ss_family == AF_INET) {
+    return AddrToName(reinterpret_cast<const sockaddr_in&>(addr), ip, port);
+  }
+  if (addr.ss_family == AF_INET6) {
+    return AddrToName(reinterpret_cast<const sockaddr_in6&>(addr), ip, port);
+  }
+  char name[1] = {'\0'};
+  ip->assign(name, name);
+  return -1;
+}
+
+/**
+ * Convert a binary IPv4 address to a string.
+ * @param addr Binary address
+ * @param ip Output string (any type that has `assign(char*, char*)`)
+ * @return Error (same as `uv_inet_ntop()`).
+ */
+template <typename T>
+int AddrToName(const in_addr& addr, T* ip) {
+  char name[128];
+  int err = uv_inet_ntop(AF_INET, &addr, name, 128);
+  if (err == 0) {
+    ip->assign(name, name + std::strlen(name));
+  } else {
+    ip->assign(name, name);
+  }
+  return err;
+}
+
+/**
+ * Convert a binary IPv6 address to a string.
+ * @param addr Binary address
+ * @param ip Output string (any type that has `assign(char*, char*)`)
+ * @return Error (same as `uv_inet_ntop()`).
+ */
+template <typename T>
+int AddrToName(const in6_addr& addr, T* ip) {
+  char name[128];
+  int err = uv_inet_ntop(AF_INET6, &addr, name, 128);
+  if (err == 0) {
+    ip->assign(name, name + std::strlen(name));
+  } else {
+    ip->assign(name, name);
+  }
+  return err;
+}
+
+/**
+ * Convert a string containing an IPv4 address to a binary structure.
+ * @param ip IPv4 address string
+ * @param port Port number
+ * @param addr Output binary structure
+ * @return Error (same as `uv_ip4_addr()`).
+ */
+int NameToAddr(std::string_view ip, unsigned int port, sockaddr_in* addr);
+
+/**
+ * Convert a string containing an IPv6 address to a binary structure.
+ * @param ip IPv6 address string
+ * @param port Port number
+ * @param addr Output binary structure
+ * @return Error (same as `uv_ip6_addr()`).
+ */
+int NameToAddr(std::string_view ip, unsigned int port, sockaddr_in6* addr);
+
+/**
+ * Convert a string containing an IPv4 address to binary format.
+ * @param ip IPv4 address string
+ * @param addr Output binary
+ * @return Error (same as `uv_inet_pton()`).
+ */
+int NameToAddr(std::string_view ip, in_addr* addr);
+
+/**
+ * Convert a string containing an IPv6 address to binary format.
+ * @param ip IPv6 address string
+ * @param addr Output binary
+ * @return Error (same as `uv_inet_pton()`).
+ */
+int NameToAddr(std::string_view ip, in6_addr* addr);
+
+}  // namespace wpi::uv
+
+#endif  // WPINET_UV_UTIL_H_
diff --git a/wpinet/src/main/native/linux/AvahiClient.cpp b/wpinet/src/main/native/linux/AvahiClient.cpp
new file mode 100644
index 0000000..7a5f560
--- /dev/null
+++ b/wpinet/src/main/native/linux/AvahiClient.cpp
@@ -0,0 +1,119 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#include "AvahiClient.h"
+
+#include <wpi/mutex.h>
+
+#include <thread>
+
+#include "dlfcn.h"
+
+using namespace wpi;
+
+#define AvahiFunctionLoad(snake_name)                                          \
+  do {                                                                         \
+    snake_name =                                                               \
+        reinterpret_cast<snake_name##_func>(dlsym(lib, "avahi_" #snake_name)); \
+    if (!snake_name) {                                                         \
+      return;                                                                  \
+    }                                                                          \
+  } while (false)
+
+AvahiFunctionTable::AvahiFunctionTable() {
+  void* lib = dlopen("libavahi-common.so.3", RTLD_LAZY);
+
+  valid = false;
+
+  if (lib == nullptr) {
+    return;
+  }
+
+  AvahiFunctionLoad(threaded_poll_new);
+  AvahiFunctionLoad(threaded_poll_free);
+  AvahiFunctionLoad(threaded_poll_get);
+  AvahiFunctionLoad(threaded_poll_start);
+  AvahiFunctionLoad(threaded_poll_stop);
+  AvahiFunctionLoad(threaded_poll_lock);
+  AvahiFunctionLoad(threaded_poll_unlock);
+  AvahiFunctionLoad(string_list_new_from_array);
+  AvahiFunctionLoad(string_list_free);
+  AvahiFunctionLoad(unescape_label);
+  AvahiFunctionLoad(alternative_service_name);
+  AvahiFunctionLoad(free);
+
+  lib = dlopen("libavahi-client.so.3", RTLD_LAZY);
+
+  if (lib == nullptr) {
+    return;
+  }
+
+  AvahiFunctionLoad(client_new);
+  AvahiFunctionLoad(client_free);
+  AvahiFunctionLoad(service_browser_new);
+  AvahiFunctionLoad(service_browser_get_client);
+  AvahiFunctionLoad(service_browser_free);
+  AvahiFunctionLoad(service_resolver_new);
+  AvahiFunctionLoad(service_resolver_free);
+  AvahiFunctionLoad(entry_group_new);
+  AvahiFunctionLoad(entry_group_free);
+  AvahiFunctionLoad(entry_group_add_service);
+  AvahiFunctionLoad(entry_group_add_service_strlst);
+  AvahiFunctionLoad(entry_group_reset);
+  AvahiFunctionLoad(entry_group_is_empty);
+  AvahiFunctionLoad(entry_group_commit);
+  AvahiFunctionLoad(entry_group_get_client);
+
+  valid = true;
+}
+
+AvahiFunctionTable& AvahiFunctionTable::Get() {
+  static AvahiFunctionTable table;
+  return table;
+}
+
+static wpi::mutex ThreadLoopLock;
+static std::weak_ptr<AvahiThread> ThreadLoop;
+
+std::shared_ptr<AvahiThread> AvahiThread::Get() {
+  std::scoped_lock lock{ThreadLoopLock};
+  auto locked = ThreadLoop.lock();
+  if (!locked) {
+    locked = std::make_unique<AvahiThread>(private_init{});
+    ThreadLoop = locked;
+  }
+  return locked;
+}
+
+AvahiThread::AvahiThread(const private_init&) {
+  if (!table.IsValid()) {
+    return;
+  }
+
+  threadedPoll = table.threaded_poll_new();
+  table.threaded_poll_start(threadedPoll);
+}
+
+AvahiThread::~AvahiThread() noexcept {
+  if (!table.IsValid()) {
+    return;
+  }
+
+  if (threadedPoll) {
+    table.threaded_poll_stop(threadedPoll);
+    table.threaded_poll_free(threadedPoll);
+  }
+}
+
+void AvahiThread::lock() {
+  table.threaded_poll_lock(threadedPoll);
+}
+
+void AvahiThread::unlock() {
+  table.threaded_poll_unlock(threadedPoll);
+}
+
+const AvahiPoll* AvahiThread::GetPoll() const {
+  return table.threaded_poll_get(threadedPoll);
+}
diff --git a/wpinet/src/main/native/linux/AvahiClient.h b/wpinet/src/main/native/linux/AvahiClient.h
new file mode 100644
index 0000000..1ed93a8
--- /dev/null
+++ b/wpinet/src/main/native/linux/AvahiClient.h
@@ -0,0 +1,322 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#pragma once
+
+#include <stdint.h>
+
+#include <memory>
+
+/***
+  This file is part of avahi.
+  avahi is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+  avahi is distributed in the hope that it will be useful, but WITHOUT
+  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+  or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
+  Public License for more details.
+  You should have received a copy of the GNU Lesser General Public
+  License along with avahi; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+typedef struct AvahiPoll AvahiPoll;
+
+typedef enum {
+  AVAHI_SERVER_INVALID,
+  AVAHI_SERVER_REGISTERING,
+  AVAHI_SERVER_RUNNING,
+  AVAHI_SERVER_COLLISION,
+  AVAHI_SERVER_FAILURE
+} AvahiServerState;
+
+typedef struct AvahiClient AvahiClient;
+
+typedef enum {
+  AVAHI_CLIENT_S_REGISTERING = AVAHI_SERVER_REGISTERING,
+  AVAHI_CLIENT_S_RUNNING = AVAHI_SERVER_RUNNING,
+  AVAHI_CLIENT_S_COLLISION = AVAHI_SERVER_COLLISION,
+  AVAHI_CLIENT_FAILURE = 100,
+  AVAHI_CLIENT_CONNECTING = 101
+} AvahiClientState;
+
+typedef enum {
+  AVAHI_CLIENT_IGNORE_USER_CONFIG = 1,
+  AVAHI_CLIENT_NO_FAIL = 2
+} AvahiClientFlags;
+
+typedef void (*AvahiClientCallback)(AvahiClient* s, AvahiClientState state,
+                                    void* userdata);
+
+typedef struct AvahiServiceBrowser AvahiServiceBrowser;
+
+typedef int AvahiProtocol;
+
+typedef int AvahiIfIndex;
+
+typedef enum {
+  AVAHI_BROWSER_NEW,
+  AVAHI_BROWSER_REMOVE,
+  AVAHI_BROWSER_CACHE_EXHAUSTED,
+  AVAHI_BROWSER_ALL_FOR_NOW,
+  AVAHI_BROWSER_FAILURE
+} AvahiBrowserEvent;
+
+typedef enum {
+  AVAHI_LOOKUP_RESULT_CACHED = 1,
+  AVAHI_LOOKUP_RESULT_WIDE_AREA = 2,
+  AVAHI_LOOKUP_RESULT_MULTICAST = 4,
+  AVAHI_LOOKUP_RESULT_LOCAL = 8,
+  AVAHI_LOOKUP_RESULT_OUR_OWN = 16,
+  AVAHI_LOOKUP_RESULT_STATIC = 32
+} AvahiLookupResultFlags;
+
+typedef void (*AvahiServiceBrowserCallback)(
+    AvahiServiceBrowser* b, AvahiIfIndex interface, AvahiProtocol protocol,
+    AvahiBrowserEvent event, const char* name, const char* type,
+    const char* domain, AvahiLookupResultFlags flags, void* userdata);
+
+typedef enum {
+  AVAHI_LOOKUP_USE_WIDE_AREA = 1,
+  AVAHI_LOOKUP_USE_MULTICAST = 2,
+
+  AVAHI_LOOKUP_NO_TXT = 4,
+  AVAHI_LOOKUP_NO_ADDRESS = 8
+} AvahiLookupFlags;
+
+typedef struct AvahiServiceResolver AvahiServiceResolver;
+
+typedef enum {
+  AVAHI_RESOLVER_FOUND,
+  AVAHI_RESOLVER_FAILURE
+} AvahiResolverEvent;
+
+typedef struct AvahiIPv4Address {
+  uint32_t address;
+} AvahiIPv4Address;
+
+typedef struct AvahiIPv6Address {
+  uint8_t address[16];
+} AvahiIPv6Address;
+
+typedef struct AvahiAddress {
+  AvahiProtocol proto;
+
+  union {
+    AvahiIPv6Address ipv6;
+    AvahiIPv4Address ipv4;
+    uint8_t data[1];
+  } data;
+} AvahiAddress;
+
+typedef struct AvahiStringList {
+  struct AvahiStringList* next;
+  size_t size;
+  uint8_t text[1];
+} AvahiStringList;
+
+typedef void (*AvahiServiceResolverCallback)(
+    AvahiServiceResolver* r, AvahiIfIndex interface, AvahiProtocol protocol,
+    AvahiResolverEvent event, const char* name, const char* type,
+    const char* domain, const char* host_name, const AvahiAddress* a,
+    uint16_t port, AvahiStringList* txt, AvahiLookupResultFlags flags,
+    void* userdata);
+
+typedef struct AvahiThreadedPoll AvahiThreadedPoll;
+
+typedef struct AvahiEntryGroup AvahiEntryGroup;
+
+typedef enum {
+  AVAHI_ENTRY_GROUP_UNCOMMITED,
+  AVAHI_ENTRY_GROUP_REGISTERING,
+  AVAHI_ENTRY_GROUP_ESTABLISHED,
+  AVAHI_ENTRY_GROUP_COLLISION,
+  AVAHI_ENTRY_GROUP_FAILURE
+} AvahiEntryGroupState;
+
+typedef void (*AvahiEntryGroupCallback)(AvahiEntryGroup* g,
+                                        AvahiEntryGroupState state,
+                                        void* userdata);
+
+typedef enum {
+  AVAHI_PUBLISH_UNIQUE = 1,
+  AVAHI_PUBLISH_NO_PROBE = 2,
+  AVAHI_PUBLISH_NO_ANNOUNCE = 4,
+  AVAHI_PUBLISH_ALLOW_MULTIPLE = 8,
+
+  AVAHI_PUBLISH_NO_REVERSE = 16,
+  AVAHI_PUBLISH_NO_COOKIE = 32,
+  AVAHI_PUBLISH_UPDATE = 64,
+  AVAHI_PUBLISH_USE_WIDE_AREA = 128,
+  AVAHI_PUBLISH_USE_MULTICAST = 256
+} AvahiPublishFlags;
+
+enum { AVAHI_IF_UNSPEC = -1 };
+
+enum { AVAHI_PROTO_INET = 0, AVAHI_PROTO_INET6 = 1, AVAHI_PROTO_UNSPEC = -1 };
+
+enum {
+  AVAHI_OK = 0,
+  AVAHI_ERR_FAILURE = -1,
+  AVAHI_ERR_BAD_STATE = -2,
+  AVAHI_ERR_INVALID_HOST_NAME = -3,
+  AVAHI_ERR_INVALID_DOMAIN_NAME = -4,
+  AVAHI_ERR_NO_NETWORK = -5,
+  AVAHI_ERR_INVALID_TTL = -6,
+  AVAHI_ERR_IS_PATTERN = -7,
+  AVAHI_ERR_COLLISION = -8,
+  AVAHI_ERR_INVALID_RECORD = -9,
+
+  AVAHI_ERR_INVALID_SERVICE_NAME = -10,
+  AVAHI_ERR_INVALID_SERVICE_TYPE = -11,
+  AVAHI_ERR_INVALID_PORT = -12,
+  AVAHI_ERR_INVALID_KEY = -13,
+  AVAHI_ERR_INVALID_ADDRESS = -14,
+  AVAHI_ERR_TIMEOUT = -15,
+  AVAHI_ERR_TOO_MANY_CLIENTS = -16,
+  AVAHI_ERR_TOO_MANY_OBJECTS = -17,
+  AVAHI_ERR_TOO_MANY_ENTRIES = -18,
+  AVAHI_ERR_OS = -19,
+
+  AVAHI_ERR_ACCESS_DENIED = -20,
+  AVAHI_ERR_INVALID_OPERATION = -21,
+  AVAHI_ERR_DBUS_ERROR = -22,
+  AVAHI_ERR_DISCONNECTED = -23,
+  AVAHI_ERR_NO_MEMORY = -24,
+  AVAHI_ERR_INVALID_OBJECT = -25,
+  AVAHI_ERR_NO_DAEMON = -26,
+  AVAHI_ERR_INVALID_INTERFACE = -27,
+  AVAHI_ERR_INVALID_PROTOCOL = -28,
+  AVAHI_ERR_INVALID_FLAGS = -29,
+
+  AVAHI_ERR_NOT_FOUND = -30,
+  AVAHI_ERR_INVALID_CONFIG = -31,
+  AVAHI_ERR_VERSION_MISMATCH = -32,
+  AVAHI_ERR_INVALID_SERVICE_SUBTYPE = -33,
+  AVAHI_ERR_INVALID_PACKET = -34,
+  AVAHI_ERR_INVALID_DNS_ERROR = -35,
+  AVAHI_ERR_DNS_FORMERR = -36,
+  AVAHI_ERR_DNS_SERVFAIL = -37,
+  AVAHI_ERR_DNS_NXDOMAIN = -38,
+  AVAHI_ERR_DNS_NOTIMP = -39,
+
+  AVAHI_ERR_DNS_REFUSED = -40,
+  AVAHI_ERR_DNS_YXDOMAIN = -41,
+  AVAHI_ERR_DNS_YXRRSET = -42,
+  AVAHI_ERR_DNS_NXRRSET = -43,
+  AVAHI_ERR_DNS_NOTAUTH = -44,
+  AVAHI_ERR_DNS_NOTZONE = -45,
+  AVAHI_ERR_INVALID_RDATA = -46,
+  AVAHI_ERR_INVALID_DNS_CLASS = -47,
+  AVAHI_ERR_INVALID_DNS_TYPE = -48,
+  AVAHI_ERR_NOT_SUPPORTED = -49,
+
+  AVAHI_ERR_NOT_PERMITTED = -50,
+  AVAHI_ERR_INVALID_ARGUMENT = -51,
+  AVAHI_ERR_IS_EMPTY = -52,
+  AVAHI_ERR_NO_CHANGE = -53,
+
+  AVAHI_ERR_MAX = -54
+};
+
+namespace wpi {
+class AvahiFunctionTable {
+ public:
+#define AvahiFunction(CapName, RetType, Parameters) \
+  using CapName##_func = RetType(*) Parameters;     \
+  CapName##_func CapName = nullptr
+
+  AvahiFunction(threaded_poll_new, AvahiThreadedPoll*, (void));
+  AvahiFunction(threaded_poll_free, void, (AvahiThreadedPoll*));
+  AvahiFunction(threaded_poll_get, const AvahiPoll*, (AvahiThreadedPoll*));
+  AvahiFunction(threaded_poll_start, int, (AvahiThreadedPoll*));
+  AvahiFunction(threaded_poll_stop, int, (AvahiThreadedPoll*));
+  AvahiFunction(threaded_poll_lock, int, (AvahiThreadedPoll*));
+  AvahiFunction(threaded_poll_unlock, int, (AvahiThreadedPoll*));
+
+  AvahiFunction(client_new, AvahiClient*,
+                (const AvahiPoll* poll_api, AvahiClientFlags flags,
+                 AvahiClientCallback callback, void* userdata, int* error));
+  AvahiFunction(client_free, void, (AvahiClient*));
+
+  AvahiFunction(service_browser_new, AvahiServiceBrowser*,
+                (AvahiClient * client, AvahiIfIndex interface,
+                 AvahiProtocol protocol, const char* type, const char* domain,
+                 AvahiLookupFlags flags, AvahiServiceBrowserCallback callback,
+                 void* userdata));
+
+  AvahiFunction(service_browser_free, int, (AvahiServiceBrowser*));
+
+  AvahiFunction(service_resolver_new, AvahiServiceResolver*,
+                (AvahiClient * client, AvahiIfIndex interface,
+                 AvahiProtocol protocol, const char* name, const char* type,
+                 const char* domain, AvahiProtocol aprotocol,
+                 AvahiLookupFlags flags, AvahiServiceResolverCallback callback,
+                 void* userdata));
+  AvahiFunction(service_resolver_free, int, (AvahiServiceResolver*));
+
+  AvahiFunction(entry_group_new, AvahiEntryGroup*,
+                (AvahiClient*, AvahiEntryGroupCallback, void*));
+  AvahiFunction(entry_group_free, int, (AvahiEntryGroup*));
+
+  AvahiFunction(entry_group_add_service, int,
+                (AvahiEntryGroup * group, AvahiIfIndex interface,
+                 AvahiProtocol protocol, AvahiPublishFlags flags,
+                 const char* name, const char* type, const char* domain,
+                 const char* host, uint16_t port, ...));
+
+  AvahiFunction(entry_group_add_service_strlst, int,
+                (AvahiEntryGroup * group, AvahiIfIndex interface,
+                 AvahiProtocol protocol, AvahiPublishFlags flags,
+                 const char* name, const char* type, const char* domain,
+                 const char* host, uint16_t port, AvahiStringList*));
+
+  AvahiFunction(entry_group_reset, int, (AvahiEntryGroup*));
+  AvahiFunction(entry_group_is_empty, int, (AvahiEntryGroup*));
+  AvahiFunction(entry_group_commit, int, (AvahiEntryGroup*));
+  AvahiFunction(entry_group_get_client, AvahiClient*, (AvahiEntryGroup*));
+
+  AvahiFunction(string_list_new_from_array, AvahiStringList*,
+                (const char** array, int len));
+  AvahiFunction(string_list_free, void, (AvahiStringList*));
+
+  AvahiFunction(service_browser_get_client, AvahiClient*,
+                (AvahiServiceBrowser*));
+
+  AvahiFunction(unescape_label, char*, (const char**, char*, size_t));
+  AvahiFunction(alternative_service_name, char*, (const char*));
+  AvahiFunction(free, void, (void*));
+
+  bool IsValid() const { return valid; }
+
+  static AvahiFunctionTable& Get();
+
+ private:
+  AvahiFunctionTable();
+  bool valid;
+};
+
+class AvahiThread {
+ private:
+  struct private_init {};
+
+ public:
+  explicit AvahiThread(const private_init&);
+  ~AvahiThread() noexcept;
+
+  void lock();
+  const AvahiPoll* GetPoll() const;
+  void unlock();
+
+  static std::shared_ptr<AvahiThread> Get();
+
+ private:
+  AvahiThreadedPoll* threadedPoll;
+  AvahiFunctionTable& table = AvahiFunctionTable::Get();
+};
+
+}  // namespace wpi
diff --git a/wpinet/src/main/native/linux/MulticastServiceAnnouncer.cpp b/wpinet/src/main/native/linux/MulticastServiceAnnouncer.cpp
new file mode 100644
index 0000000..d04711f
--- /dev/null
+++ b/wpinet/src/main/native/linux/MulticastServiceAnnouncer.cpp
@@ -0,0 +1,188 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#include "wpinet/MulticastServiceAnnouncer.h"
+
+#include <vector>
+
+#include <fmt/format.h>
+#include <wpi/mutex.h>
+
+#include "AvahiClient.h"
+
+using namespace wpi;
+
+struct MulticastServiceAnnouncer::Impl {
+  AvahiFunctionTable& table = AvahiFunctionTable::Get();
+  std::shared_ptr<AvahiThread> thread = AvahiThread::Get();
+  AvahiClient* client = nullptr;
+  AvahiEntryGroup* group = nullptr;
+  std::string serviceName;
+  std::string serviceType;
+  int port;
+  AvahiStringList* stringList = nullptr;
+
+  ~Impl() noexcept {
+    if (stringList != nullptr && table.IsValid()) {
+      table.string_list_free(stringList);
+    }
+  }
+
+  template <typename T>
+  Impl(std::string_view serviceName, std::string_view serviceType, int port,
+       std::span<const std::pair<T, T>> txt);
+};
+
+template <typename T>
+MulticastServiceAnnouncer::Impl::Impl(std::string_view serviceName,
+                                      std::string_view serviceType, int port,
+                                      std::span<const std::pair<T, T>> txt) {
+  if (!this->table.IsValid()) {
+    return;
+  }
+
+  this->serviceName = serviceName;
+  this->serviceType = serviceType;
+  this->port = port;
+
+  if (txt.empty()) {
+    this->stringList = nullptr;
+  } else {
+    std::vector<std::string> txts;
+    for (auto&& i : txt) {
+      txts.push_back(fmt::format("{}={}", i.first, i.second));
+    }
+
+    std::vector<const char*> txtArr;
+    for (auto&& i : txts) {
+      txtArr.push_back(i.c_str());
+    }
+
+    this->stringList =
+        this->table.string_list_new_from_array(txtArr.data(), txtArr.size());
+  }
+}
+
+static void RegisterService(AvahiClient* client,
+                            MulticastServiceAnnouncer::Impl* impl);
+
+static void EntryGroupCallback(AvahiEntryGroup* group,
+                               AvahiEntryGroupState state, void* userdata) {
+  if (state == AVAHI_ENTRY_GROUP_COLLISION) {
+    // Remote collision
+    MulticastServiceAnnouncer::Impl* impl =
+        reinterpret_cast<MulticastServiceAnnouncer::Impl*>(userdata);
+    char* newName =
+        impl->table.alternative_service_name(impl->serviceName.c_str());
+    impl->serviceName = newName;
+    impl->table.free(newName);
+    RegisterService(impl->table.entry_group_get_client(group), impl);
+  }
+}
+
+static void RegisterService(AvahiClient* client,
+                            MulticastServiceAnnouncer::Impl* impl) {
+  if (impl->group == nullptr) {
+    impl->group = impl->table.entry_group_new(client, EntryGroupCallback, impl);
+  }
+
+  while (true) {
+    if (impl->table.entry_group_is_empty(impl->group)) {
+      int ret = 0;
+      if (impl->stringList == nullptr) {
+        ret = impl->table.entry_group_add_service(
+            impl->group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC,
+            AVAHI_PUBLISH_USE_MULTICAST, impl->serviceName.c_str(),
+            impl->serviceType.c_str(), "local", nullptr, impl->port, nullptr);
+      } else {
+        ret = impl->table.entry_group_add_service_strlst(
+            impl->group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC,
+            AVAHI_PUBLISH_USE_MULTICAST, impl->serviceName.c_str(),
+            impl->serviceType.c_str(), "local", nullptr, impl->port,
+            impl->stringList);
+      }
+      if (ret == AVAHI_ERR_COLLISION) {
+        // Local collision
+        char* newName =
+            impl->table.alternative_service_name(impl->serviceName.c_str());
+        impl->serviceName = newName;
+        impl->table.free(newName);
+        continue;
+      } else if (ret != AVAHI_OK) {
+        break;
+      }
+      impl->table.entry_group_commit(impl->group);
+      break;
+    }
+  }
+}
+
+static void ClientCallback(AvahiClient* client, AvahiClientState state,
+                           void* userdata) {
+  MulticastServiceAnnouncer::Impl* impl =
+      reinterpret_cast<MulticastServiceAnnouncer::Impl*>(userdata);
+
+  if (state == AVAHI_CLIENT_S_RUNNING) {
+    RegisterService(client, impl);
+  } else if (state == AVAHI_CLIENT_S_COLLISION ||
+             state == AVAHI_CLIENT_S_REGISTERING) {
+    if (impl->group) {
+      impl->table.entry_group_reset(impl->group);
+    }
+  }
+}
+
+MulticastServiceAnnouncer::MulticastServiceAnnouncer(
+    std::string_view serviceName, std::string_view serviceType, int port) {
+  std::span<const std::pair<std::string_view, std::string_view>> txt;
+  pImpl = std::make_unique<Impl>(serviceName, serviceType, port, txt);
+}
+
+MulticastServiceAnnouncer::MulticastServiceAnnouncer(
+    std::string_view serviceName, std::string_view serviceType, int port,
+    std::span<const std::pair<std::string, std::string>> txt) {
+  pImpl = std::make_unique<Impl>(serviceName, serviceType, port, txt);
+}
+
+MulticastServiceAnnouncer::MulticastServiceAnnouncer(
+    std::string_view serviceName, std::string_view serviceType, int port,
+    std::span<const std::pair<std::string_view, std::string_view>> txt) {
+  pImpl = std::make_unique<Impl>(serviceName, serviceType, port, txt);
+}
+
+MulticastServiceAnnouncer::~MulticastServiceAnnouncer() noexcept {
+  Stop();
+}
+
+bool MulticastServiceAnnouncer::HasImplementation() const {
+  return pImpl->table.IsValid();
+}
+
+void MulticastServiceAnnouncer::Start() {
+  if (!pImpl->table.IsValid()) {
+    return;
+  }
+  std::scoped_lock lock{*pImpl->thread};
+  if (pImpl->client) {
+    return;
+  }
+  pImpl->client =
+      pImpl->table.client_new(pImpl->thread->GetPoll(), AVAHI_CLIENT_NO_FAIL,
+                              ClientCallback, pImpl.get(), nullptr);
+}
+
+void MulticastServiceAnnouncer::Stop() {
+  if (!pImpl->table.IsValid()) {
+    return;
+  }
+  std::scoped_lock lock{*pImpl->thread};
+  if (pImpl->client) {
+    if (pImpl->group) {
+      pImpl->table.entry_group_free(pImpl->group);
+      pImpl->group = nullptr;
+    }
+    pImpl->table.client_free(pImpl->client);
+    pImpl->client = nullptr;
+  }
+}
diff --git a/wpinet/src/main/native/linux/MulticastServiceResolver.cpp b/wpinet/src/main/native/linux/MulticastServiceResolver.cpp
new file mode 100644
index 0000000..a408639
--- /dev/null
+++ b/wpinet/src/main/native/linux/MulticastServiceResolver.cpp
@@ -0,0 +1,150 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#include "wpinet/MulticastServiceResolver.h"
+
+#include <wpi/SmallString.h>
+#include <wpi/StringExtras.h>
+#include <wpi/mutex.h>
+
+#include "AvahiClient.h"
+
+using namespace wpi;
+
+struct MulticastServiceResolver::Impl {
+  AvahiFunctionTable& table = AvahiFunctionTable::Get();
+  std::shared_ptr<AvahiThread> thread = AvahiThread::Get();
+  AvahiClient* client;
+  AvahiServiceBrowser* browser;
+  std::string serviceType;
+  MulticastServiceResolver* resolver;
+
+  void onFound(ServiceData&& data) {
+    resolver->PushData(std::forward<ServiceData>(data));
+  }
+};
+
+MulticastServiceResolver::MulticastServiceResolver(
+    std::string_view serviceType) {
+  pImpl = std::make_unique<Impl>();
+  pImpl->serviceType = serviceType;
+  pImpl->resolver = this;
+}
+
+MulticastServiceResolver::~MulticastServiceResolver() noexcept {
+  Stop();
+}
+
+bool MulticastServiceResolver::HasImplementation() const {
+  return pImpl->table.IsValid();
+}
+
+static void ResolveCallback(AvahiServiceResolver* r, AvahiIfIndex interface,
+                            AvahiProtocol protocol, AvahiResolverEvent event,
+                            const char* name, const char* type,
+                            const char* domain, const char* host_name,
+                            const AvahiAddress* address, uint16_t port,
+                            AvahiStringList* txt, AvahiLookupResultFlags flags,
+                            void* userdata) {
+  MulticastServiceResolver::Impl* impl =
+      reinterpret_cast<MulticastServiceResolver::Impl*>(userdata);
+
+  if (event == AVAHI_RESOLVER_FOUND) {
+    if (address->proto == AVAHI_PROTO_INET) {
+      AvahiStringList* strLst = txt;
+      MulticastServiceResolver::ServiceData data;
+      while (strLst != nullptr) {
+        std::string_view value{reinterpret_cast<const char*>(strLst->text),
+                               strLst->size};
+        strLst = strLst->next;
+        size_t splitIndex = value.find('=');
+        if (splitIndex == value.npos) {
+          // Todo make this just do key
+          continue;
+        }
+        std::string_view key = wpi::substr(value, 0, splitIndex);
+        value =
+            wpi::substr(value, splitIndex + 1, value.size() - splitIndex - 1);
+        data.txt.emplace_back(std::pair<std::string, std::string>{key, value});
+      }
+      wpi::SmallString<256> outputHostName;
+      char label[256];
+      do {
+        impl->table.unescape_label(&host_name, label, sizeof(label));
+        if (label[0] == '\0') {
+          break;
+        }
+        outputHostName.append(label);
+        outputHostName.append(".");
+      } while (true);
+
+      data.ipv4Address = address->data.ipv4.address;
+      data.port = port;
+      data.serviceName = name;
+      data.hostName = std::string{outputHostName};
+
+      impl->onFound(std::move(data));
+    }
+  }
+
+  impl->table.service_resolver_free(r);
+}
+
+static void BrowseCallback(AvahiServiceBrowser* b, AvahiIfIndex interface,
+                           AvahiProtocol protocol, AvahiBrowserEvent event,
+                           const char* name, const char* type,
+                           const char* domain, AvahiLookupResultFlags flags,
+                           void* userdata) {
+  MulticastServiceResolver::Impl* impl =
+      reinterpret_cast<MulticastServiceResolver::Impl*>(userdata);
+
+  if (event == AVAHI_BROWSER_NEW) {
+    impl->table.service_resolver_new(
+        impl->table.service_browser_get_client(b), interface, protocol, name,
+        type, domain, AVAHI_PROTO_UNSPEC, AVAHI_LOOKUP_USE_MULTICAST,
+        ResolveCallback, userdata);
+  }
+}
+
+static void ClientCallback(AvahiClient* client, AvahiClientState state,
+                           void* userdata) {
+  MulticastServiceResolver::Impl* impl =
+      reinterpret_cast<MulticastServiceResolver::Impl*>(userdata);
+
+  if (state == AVAHI_CLIENT_S_RUNNING) {
+    impl->browser = impl->table.service_browser_new(
+        client, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, impl->serviceType.c_str(),
+        "local", AvahiLookupFlags::AVAHI_LOOKUP_USE_MULTICAST, BrowseCallback,
+        userdata);
+  }
+}
+
+void MulticastServiceResolver::Start() {
+  if (!pImpl->table.IsValid()) {
+    return;
+  }
+  std::scoped_lock lock{*pImpl->thread};
+  if (pImpl->client) {
+    return;
+  }
+
+  pImpl->client =
+      pImpl->table.client_new(pImpl->thread->GetPoll(), AVAHI_CLIENT_NO_FAIL,
+                              ClientCallback, pImpl.get(), nullptr);
+}
+
+void MulticastServiceResolver::Stop() {
+  if (!pImpl->table.IsValid()) {
+    return;
+  }
+  std::scoped_lock lock{*pImpl->thread};
+  if (pImpl->client) {
+    if (pImpl->browser) {
+      pImpl->table.service_browser_free(pImpl->browser);
+      pImpl->browser = nullptr;
+    }
+    pImpl->table.client_free(pImpl->client);
+    pImpl->client = nullptr;
+  }
+}
diff --git a/wpinet/src/main/native/macOS/MulticastServiceAnnouncer.cpp b/wpinet/src/main/native/macOS/MulticastServiceAnnouncer.cpp
new file mode 100644
index 0000000..8c31a3b
--- /dev/null
+++ b/wpinet/src/main/native/macOS/MulticastServiceAnnouncer.cpp
@@ -0,0 +1,96 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#include "wpinet/MulticastServiceAnnouncer.h"
+
+#include <arpa/inet.h>
+
+#include <wpi/SmallString.h>
+
+#include "dns_sd.h"
+
+using namespace wpi;
+
+struct MulticastServiceAnnouncer::Impl {
+  std::string serviceName;
+  std::string serviceType;
+  int port;
+  DNSServiceRef serviceRef{nullptr};
+  TXTRecordRef txtRecord;
+
+  Impl() { TXTRecordCreate(&txtRecord, 0, nullptr); }
+
+  ~Impl() noexcept { TXTRecordDeallocate(&txtRecord); }
+};
+
+MulticastServiceAnnouncer::MulticastServiceAnnouncer(
+    std::string_view serviceName, std::string_view serviceType, int port) {
+  pImpl = std::make_unique<Impl>();
+  pImpl->serviceName = serviceName;
+  pImpl->serviceType = serviceType;
+  pImpl->port = port;
+}
+
+MulticastServiceAnnouncer::MulticastServiceAnnouncer(
+    std::string_view serviceName, std::string_view serviceType, int port,
+    std::span<const std::pair<std::string, std::string>> txt) {
+  pImpl = std::make_unique<Impl>();
+  pImpl->serviceName = serviceName;
+  pImpl->serviceType = serviceType;
+  pImpl->port = port;
+
+  for (auto&& i : txt) {
+    TXTRecordSetValue(&pImpl->txtRecord, i.first.c_str(), i.second.length(),
+                      i.second.c_str());
+  }
+}
+
+MulticastServiceAnnouncer::MulticastServiceAnnouncer(
+    std::string_view serviceName, std::string_view serviceType, int port,
+    std::span<const std::pair<std::string_view, std::string_view>> txt) {
+  pImpl = std::make_unique<Impl>();
+  pImpl->serviceName = serviceName;
+  pImpl->serviceType = serviceType;
+  pImpl->port = port;
+
+  wpi::SmallString<64> key;
+
+  for (auto&& i : txt) {
+    key.clear();
+    key.append(i.first);
+    key.emplace_back('\0');
+
+    TXTRecordSetValue(&pImpl->txtRecord, key.data(), i.second.length(),
+                      i.second.data());
+  }
+}
+
+MulticastServiceAnnouncer::~MulticastServiceAnnouncer() noexcept {
+  Stop();
+}
+
+bool MulticastServiceAnnouncer::HasImplementation() const {
+  return true;
+}
+
+void MulticastServiceAnnouncer::Start() {
+  if (pImpl->serviceRef) {
+    return;
+  }
+
+  uint16_t len = TXTRecordGetLength(&pImpl->txtRecord);
+  const void* ptr = TXTRecordGetBytesPtr(&pImpl->txtRecord);
+
+  (void)DNSServiceRegister(&pImpl->serviceRef, 0, 0, pImpl->serviceName.c_str(),
+                           pImpl->serviceType.c_str(), "local", nullptr,
+                           htons(pImpl->port), len, ptr, nullptr, nullptr);
+}
+
+void MulticastServiceAnnouncer::Stop() {
+  if (!pImpl->serviceRef) {
+    return;
+  }
+  DNSServiceRefDeallocate(pImpl->serviceRef);
+  pImpl->serviceRef = nullptr;
+}
diff --git a/wpinet/src/main/native/macOS/MulticastServiceResolver.cpp b/wpinet/src/main/native/macOS/MulticastServiceResolver.cpp
new file mode 100644
index 0000000..eecf819
--- /dev/null
+++ b/wpinet/src/main/native/macOS/MulticastServiceResolver.cpp
@@ -0,0 +1,218 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#if defined(__APPLE__)
+
+#include "wpinet/MulticastServiceResolver.h"
+
+#include <netinet/in.h>
+#include <poll.h>
+
+#include <atomic>
+#include <thread>
+#include <vector>
+
+#include <wpi/SmallVector.h>
+
+#include "ResolverThread.h"
+#include "dns_sd.h"
+
+using namespace wpi;
+
+struct DnsResolveState {
+  DnsResolveState(MulticastServiceResolver::Impl* impl,
+                  std::string_view serviceNameView)
+      : pImpl{impl} {
+    data.serviceName = serviceNameView;
+  }
+
+  DNSServiceRef ResolveRef = nullptr;
+  MulticastServiceResolver::Impl* pImpl;
+
+  MulticastServiceResolver::ServiceData data;
+};
+
+struct MulticastServiceResolver::Impl {
+  std::string serviceType;
+  MulticastServiceResolver* resolver;
+  std::shared_ptr<ResolverThread> thread = ResolverThread::Get();
+  std::vector<std::unique_ptr<DnsResolveState>> ResolveStates;
+  DNSServiceRef serviceRef = nullptr;
+
+  void onFound(ServiceData&& data) {
+    resolver->PushData(std::forward<ServiceData>(data));
+  }
+};
+
+MulticastServiceResolver::MulticastServiceResolver(
+    std::string_view serviceType) {
+  pImpl = std::make_unique<Impl>();
+  pImpl->serviceType = serviceType;
+  pImpl->resolver = this;
+}
+
+MulticastServiceResolver::~MulticastServiceResolver() noexcept {
+  Stop();
+}
+
+void ServiceGetAddrInfoReply(DNSServiceRef sdRef, DNSServiceFlags flags,
+                             uint32_t interfaceIndex,
+                             DNSServiceErrorType errorCode,
+                             const char* hostname,
+                             const struct sockaddr* address, uint32_t ttl,
+                             void* context) {
+  if (errorCode != kDNSServiceErr_NoError) {
+    return;
+  }
+
+  DnsResolveState* resolveState = static_cast<DnsResolveState*>(context);
+
+  resolveState->data.hostName = hostname;
+  resolveState->data.ipv4Address =
+      reinterpret_cast<const struct sockaddr_in*>(address)->sin_addr.s_addr;
+
+  resolveState->pImpl->onFound(std::move(resolveState->data));
+
+  resolveState->pImpl->thread->RemoveServiceRefInThread(
+      resolveState->ResolveRef);
+
+  resolveState->pImpl->ResolveStates.erase(std::find_if(
+      resolveState->pImpl->ResolveStates.begin(),
+      resolveState->pImpl->ResolveStates.end(),
+      [resolveState](auto& a) { return a.get() == resolveState; }));
+}
+
+void ServiceResolveReply(DNSServiceRef sdRef, DNSServiceFlags flags,
+                         uint32_t interfaceIndex, DNSServiceErrorType errorCode,
+                         const char* fullname, const char* hosttarget,
+                         uint16_t port, /* In network byte order */
+                         uint16_t txtLen, const unsigned char* txtRecord,
+                         void* context) {
+  if (errorCode != kDNSServiceErr_NoError) {
+    return;
+  }
+
+  DnsResolveState* resolveState = static_cast<DnsResolveState*>(context);
+  resolveState->pImpl->thread->RemoveServiceRefInThread(
+      resolveState->ResolveRef);
+  DNSServiceRefDeallocate(resolveState->ResolveRef);
+  resolveState->ResolveRef = nullptr;
+  resolveState->data.port = ntohs(port);
+
+  int txtCount = TXTRecordGetCount(txtLen, txtRecord);
+  char keyBuf[256];
+  uint8_t valueLen;
+  const void* value;
+
+  for (int i = 0; i < txtCount; i++) {
+    errorCode = TXTRecordGetItemAtIndex(txtLen, txtRecord, i, sizeof(keyBuf),
+                                        keyBuf, &valueLen, &value);
+    if (errorCode == kDNSServiceErr_NoError) {
+      if (valueLen == 0) {
+        // No value
+        resolveState->data.txt.emplace_back(
+            std::pair<std::string, std::string>{std::string{keyBuf}, {}});
+      } else {
+        resolveState->data.txt.emplace_back(std::pair<std::string, std::string>{
+            std::string{keyBuf},
+            std::string{reinterpret_cast<const char*>(value), valueLen}});
+      }
+    }
+  }
+
+  errorCode = DNSServiceGetAddrInfo(
+      &resolveState->ResolveRef, flags, interfaceIndex,
+      kDNSServiceProtocol_IPv4, hosttarget, ServiceGetAddrInfoReply, context);
+
+  if (errorCode == kDNSServiceErr_NoError) {
+    dnssd_sock_t socket = DNSServiceRefSockFD(resolveState->ResolveRef);
+    resolveState->pImpl->thread->AddServiceRef(resolveState->ResolveRef,
+                                               socket);
+  } else {
+    resolveState->pImpl->thread->RemoveServiceRefInThread(
+        resolveState->ResolveRef);
+    resolveState->pImpl->ResolveStates.erase(std::find_if(
+        resolveState->pImpl->ResolveStates.begin(),
+        resolveState->pImpl->ResolveStates.end(),
+        [resolveState](auto& a) { return a.get() == resolveState; }));
+  }
+}
+
+static void DnsCompletion(DNSServiceRef sdRef, DNSServiceFlags flags,
+                          uint32_t interfaceIndex,
+                          DNSServiceErrorType errorCode,
+                          const char* serviceName, const char* regtype,
+                          const char* replyDomain, void* context) {
+  if (errorCode != kDNSServiceErr_NoError) {
+    return;
+  }
+  if (!(flags & kDNSServiceFlagsAdd)) {
+    return;
+  }
+
+  MulticastServiceResolver::Impl* impl =
+      static_cast<MulticastServiceResolver::Impl*>(context);
+  auto& resolveState = impl->ResolveStates.emplace_back(
+      std::make_unique<DnsResolveState>(impl, serviceName));
+
+  errorCode = DNSServiceResolve(&resolveState->ResolveRef, 0, interfaceIndex,
+                                serviceName, regtype, replyDomain,
+                                ServiceResolveReply, resolveState.get());
+
+  if (errorCode == kDNSServiceErr_NoError) {
+    dnssd_sock_t socket = DNSServiceRefSockFD(resolveState->ResolveRef);
+    resolveState->pImpl->thread->AddServiceRef(resolveState->ResolveRef,
+                                               socket);
+  } else {
+    resolveState->pImpl->ResolveStates.erase(std::find_if(
+        resolveState->pImpl->ResolveStates.begin(),
+        resolveState->pImpl->ResolveStates.end(),
+        [r = resolveState.get()](auto& a) { return a.get() == r; }));
+  }
+}
+
+bool MulticastServiceResolver::HasImplementation() const {
+  return true;
+}
+
+void MulticastServiceResolver::Start() {
+  if (pImpl->serviceRef) {
+    return;
+  }
+
+  DNSServiceErrorType status =
+      DNSServiceBrowse(&pImpl->serviceRef, 0, 0, pImpl->serviceType.c_str(),
+                       "local", DnsCompletion, pImpl.get());
+  if (status == kDNSServiceErr_NoError) {
+    dnssd_sock_t socket = DNSServiceRefSockFD(pImpl->serviceRef);
+    pImpl->thread->AddServiceRef(pImpl->serviceRef, socket);
+  }
+}
+
+void MulticastServiceResolver::Stop() {
+  if (!pImpl->serviceRef) {
+    return;
+  }
+  wpi::SmallVector<WPI_EventHandle, 8> cleanupEvents;
+  for (auto&& i : pImpl->ResolveStates) {
+    cleanupEvents.push_back(
+        pImpl->thread->RemoveServiceRefOutsideThread(i->ResolveRef));
+  }
+  cleanupEvents.push_back(
+      pImpl->thread->RemoveServiceRefOutsideThread(pImpl->serviceRef));
+  wpi::SmallVector<WPI_Handle, 8> signaledBuf;
+  signaledBuf.resize(cleanupEvents.size());
+  while (!cleanupEvents.empty()) {
+    auto signaled = wpi::WaitForObjects(cleanupEvents, signaledBuf);
+    for (auto&& s : signaled) {
+      cleanupEvents.erase(
+          std::find(cleanupEvents.begin(), cleanupEvents.end(), s));
+    }
+  }
+
+  pImpl->ResolveStates.clear();
+  pImpl->serviceRef = nullptr;
+}
+
+#endif  // defined(__APPLE__)
diff --git a/wpinet/src/main/native/macOS/ResolverThread.cpp b/wpinet/src/main/native/macOS/ResolverThread.cpp
new file mode 100644
index 0000000..1452670
--- /dev/null
+++ b/wpinet/src/main/native/macOS/ResolverThread.cpp
@@ -0,0 +1,115 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#if defined(__APPLE__)
+
+#include "ResolverThread.h"
+
+#include <algorithm>
+
+#include <wpi/mutex.h>
+
+using namespace wpi;
+
+ResolverThread::ResolverThread(const private_init&) {}
+
+ResolverThread::~ResolverThread() noexcept {
+  running = false;
+  if (thread.joinable()) {
+    thread.join();
+  }
+}
+
+void ResolverThread::AddServiceRef(DNSServiceRef serviceRef,
+                                   dnssd_sock_t socket) {
+  std::scoped_lock lock{serviceRefMutex};
+  serviceRefs.emplace_back(
+      std::pair<DNSServiceRef, dnssd_sock_t>{serviceRef, socket});
+  if (serviceRefs.size() == 1) {
+    running = false;
+    if (thread.joinable()) {
+      thread.join();
+    }
+    running = true;
+    thread = std::thread([=] { ThreadMain(); });
+  }
+}
+
+void ResolverThread::RemoveServiceRefInThread(DNSServiceRef serviceRef) {
+  std::scoped_lock lock{serviceRefMutex};
+  serviceRefs.erase(
+      std::find_if(serviceRefs.begin(), serviceRefs.end(),
+                   [=](auto& a) { return a.first == serviceRef; }));
+  DNSServiceRefDeallocate(serviceRef);
+}
+
+WPI_EventHandle ResolverThread::RemoveServiceRefOutsideThread(
+    DNSServiceRef serviceRef) {
+  std::scoped_lock lock{serviceRefMutex};
+  WPI_EventHandle handle = CreateEvent(true);
+  serviceRefsToRemove.push_back({serviceRef, handle});
+  return handle;
+}
+
+bool ResolverThread::CleanupRefs() {
+  std::scoped_lock lock{serviceRefMutex};
+  for (auto&& r : serviceRefsToRemove) {
+    serviceRefs.erase(
+        std::find_if(serviceRefs.begin(), serviceRefs.end(),
+                     [=](auto& a) { return a.first == r.first; }));
+    DNSServiceRefDeallocate(r.first);
+    wpi::SetEvent(r.second);
+  }
+  serviceRefsToRemove.clear();
+  return serviceRefs.empty();
+}
+
+void ResolverThread::ThreadMain() {
+  std::vector<pollfd> readSockets;
+  std::vector<DNSServiceRef> serviceRefs;
+
+  while (running) {
+    readSockets.clear();
+    serviceRefs.clear();
+
+    for (auto&& i : this->serviceRefs) {
+      readSockets.emplace_back(pollfd{i.second, POLLIN, 0});
+      serviceRefs.emplace_back(i.first);
+    }
+
+    int res = poll(readSockets.begin().base(), readSockets.size(), 100);
+
+    if (res > 0) {
+      for (size_t i = 0; i < readSockets.size(); i++) {
+        if (readSockets[i].revents == POLLIN) {
+          DNSServiceProcessResult(serviceRefs[i]);
+        }
+      }
+    } else if (res == 0) {
+      if (!running) {
+        CleanupRefs();
+        break;
+      }
+    }
+
+    if (CleanupRefs()) {
+      break;
+    }
+  }
+}
+
+static wpi::mutex ThreadLoopLock;
+static std::weak_ptr<ResolverThread> ThreadLoop;
+
+std::shared_ptr<ResolverThread> ResolverThread::Get() {
+  std::scoped_lock lock{ThreadLoopLock};
+  auto locked = ThreadLoop.lock();
+  if (!locked) {
+    locked = std::make_unique<ResolverThread>(private_init{});
+    ThreadLoop = locked;
+  }
+  return locked;
+}
+
+#endif  // defined(__APPLE__)
diff --git a/wpinet/src/main/native/macOS/ResolverThread.h b/wpinet/src/main/native/macOS/ResolverThread.h
new file mode 100644
index 0000000..158cda8
--- /dev/null
+++ b/wpinet/src/main/native/macOS/ResolverThread.h
@@ -0,0 +1,50 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#if defined(__APPLE__)
+
+#pragma once
+
+#include <netinet/in.h>
+#include <poll.h>
+
+#include <atomic>
+#include <memory>
+#include <thread>
+#include <utility>
+#include <vector>
+
+#include <wpi/Synchronization.h>
+#include <wpi/mutex.h>
+
+#include "dns_sd.h"
+
+namespace wpi {
+class ResolverThread {
+ private:
+  struct private_init {};
+
+ public:
+  explicit ResolverThread(const private_init&);
+  ~ResolverThread() noexcept;
+
+  void AddServiceRef(DNSServiceRef serviceRef, dnssd_sock_t socket);
+  void RemoveServiceRefInThread(DNSServiceRef serviceRef);
+  WPI_EventHandle RemoveServiceRefOutsideThread(DNSServiceRef serviceRef);
+
+  static std::shared_ptr<ResolverThread> Get();
+
+ private:
+  void ThreadMain();
+  bool CleanupRefs();
+
+  wpi::mutex serviceRefMutex;
+  std::vector<std::pair<DNSServiceRef, WPI_EventHandle>> serviceRefsToRemove;
+  std::vector<std::pair<DNSServiceRef, dnssd_sock_t>> serviceRefs;
+  std::thread thread;
+  std::atomic_bool running;
+};
+}  // namespace wpi
+
+#endif  // defined(__APPLE__)
diff --git a/wpinet/src/main/native/resources/bootstrap-4.1.min.js.gz b/wpinet/src/main/native/resources/bootstrap-4.1.min.js.gz
new file mode 100644
index 0000000..a7797e1
--- /dev/null
+++ b/wpinet/src/main/native/resources/bootstrap-4.1.min.js.gz
Binary files differ
diff --git a/wpinet/src/main/native/resources/coreui-2.1.min.css.gz b/wpinet/src/main/native/resources/coreui-2.1.min.css.gz
new file mode 100644
index 0000000..d4f43ad
--- /dev/null
+++ b/wpinet/src/main/native/resources/coreui-2.1.min.css.gz
Binary files differ
diff --git a/wpinet/src/main/native/resources/coreui-2.1.min.js.gz b/wpinet/src/main/native/resources/coreui-2.1.min.js.gz
new file mode 100644
index 0000000..cf4ccb1
--- /dev/null
+++ b/wpinet/src/main/native/resources/coreui-2.1.min.js.gz
Binary files differ
diff --git a/wpinet/src/main/native/resources/feather-4.8.min.js.gz b/wpinet/src/main/native/resources/feather-4.8.min.js.gz
new file mode 100644
index 0000000..d275865
--- /dev/null
+++ b/wpinet/src/main/native/resources/feather-4.8.min.js.gz
Binary files differ
diff --git a/wpinet/src/main/native/resources/jquery-3.3.slim.min.js.gz b/wpinet/src/main/native/resources/jquery-3.3.slim.min.js.gz
new file mode 100644
index 0000000..712e06c
--- /dev/null
+++ b/wpinet/src/main/native/resources/jquery-3.3.slim.min.js.gz
Binary files differ
diff --git a/wpinet/src/main/native/resources/popper-1.14.min.js.gz b/wpinet/src/main/native/resources/popper-1.14.min.js.gz
new file mode 100644
index 0000000..c6ebec8
--- /dev/null
+++ b/wpinet/src/main/native/resources/popper-1.14.min.js.gz
Binary files differ
diff --git a/wpinet/src/main/native/thirdparty/libuv/include/uv.h b/wpinet/src/main/native/thirdparty/libuv/include/uv.h
new file mode 100644
index 0000000..dbaeb1e
--- /dev/null
+++ b/wpinet/src/main/native/thirdparty/libuv/include/uv.h
@@ -0,0 +1,1834 @@
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+/* See https://github.com/libuv/libuv#documentation for documentation. */
+
+#ifndef UV_H
+#define UV_H
+
+#if defined(BUILDING_UV_SHARED) && defined(USING_UV_SHARED)
+#error "Define either BUILDING_UV_SHARED or USING_UV_SHARED, not both."
+#endif
+
+#ifdef _WIN32
+  /* Windows - set up dll import/export decorators. */
+# if defined(BUILDING_UV_SHARED)
+    /* Building shared library. */
+#   define UV_EXTERN __declspec(dllexport)
+# elif defined(USING_UV_SHARED)
+    /* Using shared library. */
+#   define UV_EXTERN __declspec(dllimport)
+# else
+    /* Building static library. */
+#   define UV_EXTERN /* nothing */
+# endif
+#elif __GNUC__ >= 4
+# define UV_EXTERN __attribute__((visibility("default")))
+#elif defined(__SUNPRO_C) && (__SUNPRO_C >= 0x550) /* Sun Studio >= 8 */
+# define UV_EXTERN __global
+#else
+# define UV_EXTERN /* nothing */
+#endif
+
+#include "uv/errno.h"
+#include "uv/version.h"
+#include <stddef.h>
+#include <stdio.h>
+
+#include <stdint.h>
+
+#if defined(_WIN32)
+# include "uv/win.h"
+#else
+# include "uv/unix.h"
+#endif
+
+/* Expand this list if necessary. */
+#define UV_ERRNO_MAP(XX)                                                      \
+  XX(E2BIG, "argument list too long")                                         \
+  XX(EACCES, "permission denied")                                             \
+  XX(EADDRINUSE, "address already in use")                                    \
+  XX(EADDRNOTAVAIL, "address not available")                                  \
+  XX(EAFNOSUPPORT, "address family not supported")                            \
+  XX(EAGAIN, "resource temporarily unavailable")                              \
+  XX(EAI_ADDRFAMILY, "address family not supported")                          \
+  XX(EAI_AGAIN, "temporary failure")                                          \
+  XX(EAI_BADFLAGS, "bad ai_flags value")                                      \
+  XX(EAI_BADHINTS, "invalid value for hints")                                 \
+  XX(EAI_CANCELED, "request canceled")                                        \
+  XX(EAI_FAIL, "permanent failure")                                           \
+  XX(EAI_FAMILY, "ai_family not supported")                                   \
+  XX(EAI_MEMORY, "out of memory")                                             \
+  XX(EAI_NODATA, "no address")                                                \
+  XX(EAI_NONAME, "unknown node or service")                                   \
+  XX(EAI_OVERFLOW, "argument buffer overflow")                                \
+  XX(EAI_PROTOCOL, "resolved protocol is unknown")                            \
+  XX(EAI_SERVICE, "service not available for socket type")                    \
+  XX(EAI_SOCKTYPE, "socket type not supported")                               \
+  XX(EALREADY, "connection already in progress")                              \
+  XX(EBADF, "bad file descriptor")                                            \
+  XX(EBUSY, "resource busy or locked")                                        \
+  XX(ECANCELED, "operation canceled")                                         \
+  XX(ECHARSET, "invalid Unicode character")                                   \
+  XX(ECONNABORTED, "software caused connection abort")                        \
+  XX(ECONNREFUSED, "connection refused")                                      \
+  XX(ECONNRESET, "connection reset by peer")                                  \
+  XX(EDESTADDRREQ, "destination address required")                            \
+  XX(EEXIST, "file already exists")                                           \
+  XX(EFAULT, "bad address in system call argument")                           \
+  XX(EFBIG, "file too large")                                                 \
+  XX(EHOSTUNREACH, "host is unreachable")                                     \
+  XX(EINTR, "interrupted system call")                                        \
+  XX(EINVAL, "invalid argument")                                              \
+  XX(EIO, "i/o error")                                                        \
+  XX(EISCONN, "socket is already connected")                                  \
+  XX(EISDIR, "illegal operation on a directory")                              \
+  XX(ELOOP, "too many symbolic links encountered")                            \
+  XX(EMFILE, "too many open files")                                           \
+  XX(EMSGSIZE, "message too long")                                            \
+  XX(ENAMETOOLONG, "name too long")                                           \
+  XX(ENETDOWN, "network is down")                                             \
+  XX(ENETUNREACH, "network is unreachable")                                   \
+  XX(ENFILE, "file table overflow")                                           \
+  XX(ENOBUFS, "no buffer space available")                                    \
+  XX(ENODEV, "no such device")                                                \
+  XX(ENOENT, "no such file or directory")                                     \
+  XX(ENOMEM, "not enough memory")                                             \
+  XX(ENONET, "machine is not on the network")                                 \
+  XX(ENOPROTOOPT, "protocol not available")                                   \
+  XX(ENOSPC, "no space left on device")                                       \
+  XX(ENOSYS, "function not implemented")                                      \
+  XX(ENOTCONN, "socket is not connected")                                     \
+  XX(ENOTDIR, "not a directory")                                              \
+  XX(ENOTEMPTY, "directory not empty")                                        \
+  XX(ENOTSOCK, "socket operation on non-socket")                              \
+  XX(ENOTSUP, "operation not supported on socket")                            \
+  XX(EOVERFLOW, "value too large for defined data type")                      \
+  XX(EPERM, "operation not permitted")                                        \
+  XX(EPIPE, "broken pipe")                                                    \
+  XX(EPROTO, "protocol error")                                                \
+  XX(EPROTONOSUPPORT, "protocol not supported")                               \
+  XX(EPROTOTYPE, "protocol wrong type for socket")                            \
+  XX(ERANGE, "result too large")                                              \
+  XX(EROFS, "read-only file system")                                          \
+  XX(ESHUTDOWN, "cannot send after transport endpoint shutdown")              \
+  XX(ESPIPE, "invalid seek")                                                  \
+  XX(ESRCH, "no such process")                                                \
+  XX(ETIMEDOUT, "connection timed out")                                       \
+  XX(ETXTBSY, "text file is busy")                                            \
+  XX(EXDEV, "cross-device link not permitted")                                \
+  XX(UNKNOWN, "unknown error")                                                \
+  XX(EOF, "end of file")                                                      \
+  XX(ENXIO, "no such device or address")                                      \
+  XX(EMLINK, "too many links")                                                \
+  XX(EHOSTDOWN, "host is down")                                               \
+  XX(EREMOTEIO, "remote I/O error")                                           \
+  XX(ENOTTY, "inappropriate ioctl for device")                                \
+  XX(EFTYPE, "inappropriate file type or format")                             \
+  XX(EILSEQ, "illegal byte sequence")                                         \
+  XX(ESOCKTNOSUPPORT, "socket type not supported")                            \
+
+#define UV_HANDLE_TYPE_MAP(XX)                                                \
+  XX(ASYNC, async)                                                            \
+  XX(CHECK, check)                                                            \
+  XX(FS_EVENT, fs_event)                                                      \
+  XX(FS_POLL, fs_poll)                                                        \
+  XX(HANDLE, handle)                                                          \
+  XX(IDLE, idle)                                                              \
+  XX(NAMED_PIPE, pipe)                                                        \
+  XX(POLL, poll)                                                              \
+  XX(PREPARE, prepare)                                                        \
+  XX(PROCESS, process)                                                        \
+  XX(STREAM, stream)                                                          \
+  XX(TCP, tcp)                                                                \
+  XX(TIMER, timer)                                                            \
+  XX(TTY, tty)                                                                \
+  XX(UDP, udp)                                                                \
+  XX(SIGNAL, signal)                                                          \
+
+#define UV_REQ_TYPE_MAP(XX)                                                   \
+  XX(REQ, req)                                                                \
+  XX(CONNECT, connect)                                                        \
+  XX(WRITE, write)                                                            \
+  XX(SHUTDOWN, shutdown)                                                      \
+  XX(UDP_SEND, udp_send)                                                      \
+  XX(FS, fs)                                                                  \
+  XX(WORK, work)                                                              \
+  XX(GETADDRINFO, getaddrinfo)                                                \
+  XX(GETNAMEINFO, getnameinfo)                                                \
+  XX(RANDOM, random)                                                          \
+
+typedef enum {
+#define XX(code, _) UV_ ## code = UV__ ## code,
+  UV_ERRNO_MAP(XX)
+#undef XX
+  UV_ERRNO_MAX = UV__EOF - 1
+} uv_errno_t;
+
+typedef enum {
+  UV_UNKNOWN_HANDLE = 0,
+#define XX(uc, lc) UV_##uc,
+  UV_HANDLE_TYPE_MAP(XX)
+#undef XX
+  UV_FILE,
+  UV_HANDLE_TYPE_MAX
+} uv_handle_type;
+
+typedef enum {
+  UV_UNKNOWN_REQ = 0,
+#define XX(uc, lc) UV_##uc,
+  UV_REQ_TYPE_MAP(XX)
+#undef XX
+  UV_REQ_TYPE_PRIVATE
+  UV_REQ_TYPE_MAX
+} uv_req_type;
+
+
+/* Handle types. */
+typedef struct uv_loop_s uv_loop_t;
+typedef struct uv_handle_s uv_handle_t;
+typedef struct uv_dir_s uv_dir_t;
+typedef struct uv_stream_s uv_stream_t;
+typedef struct uv_tcp_s uv_tcp_t;
+typedef struct uv_udp_s uv_udp_t;
+typedef struct uv_pipe_s uv_pipe_t;
+typedef struct uv_tty_s uv_tty_t;
+typedef struct uv_poll_s uv_poll_t;
+typedef struct uv_timer_s uv_timer_t;
+typedef struct uv_prepare_s uv_prepare_t;
+typedef struct uv_check_s uv_check_t;
+typedef struct uv_idle_s uv_idle_t;
+typedef struct uv_async_s uv_async_t;
+typedef struct uv_process_s uv_process_t;
+typedef struct uv_fs_event_s uv_fs_event_t;
+typedef struct uv_fs_poll_s uv_fs_poll_t;
+typedef struct uv_signal_s uv_signal_t;
+
+/* Request types. */
+typedef struct uv_req_s uv_req_t;
+typedef struct uv_getaddrinfo_s uv_getaddrinfo_t;
+typedef struct uv_getnameinfo_s uv_getnameinfo_t;
+typedef struct uv_shutdown_s uv_shutdown_t;
+typedef struct uv_write_s uv_write_t;
+typedef struct uv_connect_s uv_connect_t;
+typedef struct uv_udp_send_s uv_udp_send_t;
+typedef struct uv_fs_s uv_fs_t;
+typedef struct uv_work_s uv_work_t;
+typedef struct uv_random_s uv_random_t;
+
+/* None of the above. */
+typedef struct uv_env_item_s uv_env_item_t;
+typedef struct uv_cpu_info_s uv_cpu_info_t;
+typedef struct uv_interface_address_s uv_interface_address_t;
+typedef struct uv_dirent_s uv_dirent_t;
+typedef struct uv_passwd_s uv_passwd_t;
+typedef struct uv_utsname_s uv_utsname_t;
+typedef struct uv_statfs_s uv_statfs_t;
+
+typedef enum {
+  UV_LOOP_BLOCK_SIGNAL = 0,
+  UV_METRICS_IDLE_TIME
+} uv_loop_option;
+
+typedef enum {
+  UV_RUN_DEFAULT = 0,
+  UV_RUN_ONCE,
+  UV_RUN_NOWAIT
+} uv_run_mode;
+
+
+UV_EXTERN unsigned int uv_version(void);
+UV_EXTERN const char* uv_version_string(void);
+
+typedef void* (*uv_malloc_func)(size_t size);
+typedef void* (*uv_realloc_func)(void* ptr, size_t size);
+typedef void* (*uv_calloc_func)(size_t count, size_t size);
+typedef void (*uv_free_func)(void* ptr);
+
+UV_EXTERN void uv_library_shutdown(void);
+
+UV_EXTERN int uv_replace_allocator(uv_malloc_func malloc_func,
+                                   uv_realloc_func realloc_func,
+                                   uv_calloc_func calloc_func,
+                                   uv_free_func free_func);
+
+UV_EXTERN uv_loop_t* uv_default_loop(void);
+UV_EXTERN int uv_loop_init(uv_loop_t* loop);
+UV_EXTERN int uv_loop_close(uv_loop_t* loop);
+/*
+ * NOTE:
+ *  This function is DEPRECATED (to be removed after 0.12), users should
+ *  allocate the loop manually and use uv_loop_init instead.
+ */
+UV_EXTERN uv_loop_t* uv_loop_new(void);
+/*
+ * NOTE:
+ *  This function is DEPRECATED (to be removed after 0.12). Users should use
+ *  uv_loop_close and free the memory manually instead.
+ */
+UV_EXTERN void uv_loop_delete(uv_loop_t*);
+UV_EXTERN size_t uv_loop_size(void);
+UV_EXTERN int uv_loop_alive(const uv_loop_t* loop);
+UV_EXTERN int uv_loop_configure(uv_loop_t* loop, uv_loop_option option, ...);
+UV_EXTERN int uv_loop_fork(uv_loop_t* loop);
+
+UV_EXTERN int uv_run(uv_loop_t*, uv_run_mode mode);
+UV_EXTERN void uv_stop(uv_loop_t*);
+
+UV_EXTERN void uv_ref(uv_handle_t*);
+UV_EXTERN void uv_unref(uv_handle_t*);
+UV_EXTERN int uv_has_ref(const uv_handle_t*);
+
+UV_EXTERN void uv_update_time(uv_loop_t*);
+UV_EXTERN uint64_t uv_now(const uv_loop_t*);
+
+UV_EXTERN int uv_backend_fd(const uv_loop_t*);
+UV_EXTERN int uv_backend_timeout(const uv_loop_t*);
+
+typedef void (*uv_alloc_cb)(uv_handle_t* handle,
+                            size_t suggested_size,
+                            uv_buf_t* buf);
+typedef void (*uv_read_cb)(uv_stream_t* stream,
+                           ssize_t nread,
+                           const uv_buf_t* buf);
+typedef void (*uv_write_cb)(uv_write_t* req, int status);
+typedef void (*uv_connect_cb)(uv_connect_t* req, int status);
+typedef void (*uv_shutdown_cb)(uv_shutdown_t* req, int status);
+typedef void (*uv_connection_cb)(uv_stream_t* server, int status);
+typedef void (*uv_close_cb)(uv_handle_t* handle);
+typedef void (*uv_poll_cb)(uv_poll_t* handle, int status, int events);
+typedef void (*uv_timer_cb)(uv_timer_t* handle);
+typedef void (*uv_async_cb)(uv_async_t* handle);
+typedef void (*uv_prepare_cb)(uv_prepare_t* handle);
+typedef void (*uv_check_cb)(uv_check_t* handle);
+typedef void (*uv_idle_cb)(uv_idle_t* handle);
+typedef void (*uv_exit_cb)(uv_process_t*, int64_t exit_status, int term_signal);
+typedef void (*uv_walk_cb)(uv_handle_t* handle, void* arg);
+typedef void (*uv_fs_cb)(uv_fs_t* req);
+typedef void (*uv_work_cb)(uv_work_t* req);
+typedef void (*uv_after_work_cb)(uv_work_t* req, int status);
+typedef void (*uv_getaddrinfo_cb)(uv_getaddrinfo_t* req,
+                                  int status,
+                                  struct addrinfo* res);
+typedef void (*uv_getnameinfo_cb)(uv_getnameinfo_t* req,
+                                  int status,
+                                  const char* hostname,
+                                  const char* service);
+typedef void (*uv_random_cb)(uv_random_t* req,
+                             int status,
+                             void* buf,
+                             size_t buflen);
+
+typedef struct {
+  long tv_sec;
+  long tv_nsec;
+} uv_timespec_t;
+
+
+typedef struct {
+  uint64_t st_dev;
+  uint64_t st_mode;
+  uint64_t st_nlink;
+  uint64_t st_uid;
+  uint64_t st_gid;
+  uint64_t st_rdev;
+  uint64_t st_ino;
+  uint64_t st_size;
+  uint64_t st_blksize;
+  uint64_t st_blocks;
+  uint64_t st_flags;
+  uint64_t st_gen;
+  uv_timespec_t st_atim;
+  uv_timespec_t st_mtim;
+  uv_timespec_t st_ctim;
+  uv_timespec_t st_birthtim;
+} uv_stat_t;
+
+
+typedef void (*uv_fs_event_cb)(uv_fs_event_t* handle,
+                               const char* filename,
+                               int events,
+                               int status);
+
+typedef void (*uv_fs_poll_cb)(uv_fs_poll_t* handle,
+                              int status,
+                              const uv_stat_t* prev,
+                              const uv_stat_t* curr);
+
+typedef void (*uv_signal_cb)(uv_signal_t* handle, int signum);
+
+
+typedef enum {
+  UV_LEAVE_GROUP = 0,
+  UV_JOIN_GROUP
+} uv_membership;
+
+
+UV_EXTERN int uv_translate_sys_error(int sys_errno);
+
+UV_EXTERN const char* uv_strerror(int err);
+UV_EXTERN char* uv_strerror_r(int err, char* buf, size_t buflen);
+
+UV_EXTERN const char* uv_err_name(int err);
+UV_EXTERN char* uv_err_name_r(int err, char* buf, size_t buflen);
+
+
+#define UV_REQ_FIELDS                                                         \
+  /* public */                                                                \
+  void* data;                                                                 \
+  /* read-only */                                                             \
+  uv_req_type type;                                                           \
+  /* private */                                                               \
+  void* reserved[6];                                                          \
+  UV_REQ_PRIVATE_FIELDS                                                       \
+
+/* Abstract base class of all requests. */
+struct uv_req_s {
+  UV_REQ_FIELDS
+};
+
+
+/* Platform-specific request types. */
+UV_PRIVATE_REQ_TYPES
+
+
+UV_EXTERN int uv_shutdown(uv_shutdown_t* req,
+                          uv_stream_t* handle,
+                          uv_shutdown_cb cb);
+
+struct uv_shutdown_s {
+  UV_REQ_FIELDS
+  uv_stream_t* handle;
+  uv_shutdown_cb cb;
+  UV_SHUTDOWN_PRIVATE_FIELDS
+};
+
+
+#define UV_HANDLE_FIELDS                                                      \
+  /* public */                                                                \
+  void* data;                                                                 \
+  /* read-only */                                                             \
+  uv_loop_t* loop;                                                            \
+  uv_handle_type type;                                                        \
+  /* private */                                                               \
+  uv_close_cb close_cb;                                                       \
+  void* handle_queue[2];                                                      \
+  union {                                                                     \
+    int fd;                                                                   \
+    void* reserved[4];                                                        \
+  } u;                                                                        \
+  UV_HANDLE_PRIVATE_FIELDS                                                    \
+
+/* The abstract base class of all handles. */
+struct uv_handle_s {
+  UV_HANDLE_FIELDS
+};
+
+UV_EXTERN size_t uv_handle_size(uv_handle_type type);
+UV_EXTERN uv_handle_type uv_handle_get_type(const uv_handle_t* handle);
+UV_EXTERN const char* uv_handle_type_name(uv_handle_type type);
+UV_EXTERN void* uv_handle_get_data(const uv_handle_t* handle);
+UV_EXTERN uv_loop_t* uv_handle_get_loop(const uv_handle_t* handle);
+UV_EXTERN void uv_handle_set_data(uv_handle_t* handle, void* data);
+
+UV_EXTERN size_t uv_req_size(uv_req_type type);
+UV_EXTERN void* uv_req_get_data(const uv_req_t* req);
+UV_EXTERN void uv_req_set_data(uv_req_t* req, void* data);
+UV_EXTERN uv_req_type uv_req_get_type(const uv_req_t* req);
+UV_EXTERN const char* uv_req_type_name(uv_req_type type);
+
+UV_EXTERN int uv_is_active(const uv_handle_t* handle);
+
+UV_EXTERN void uv_walk(uv_loop_t* loop, uv_walk_cb walk_cb, void* arg);
+
+/* Helpers for ad hoc debugging, no API/ABI stability guaranteed. */
+UV_EXTERN void uv_print_all_handles(uv_loop_t* loop, FILE* stream);
+UV_EXTERN void uv_print_active_handles(uv_loop_t* loop, FILE* stream);
+
+UV_EXTERN void uv_close(uv_handle_t* handle, uv_close_cb close_cb);
+
+UV_EXTERN int uv_send_buffer_size(uv_handle_t* handle, int* value);
+UV_EXTERN int uv_recv_buffer_size(uv_handle_t* handle, int* value);
+
+UV_EXTERN int uv_fileno(const uv_handle_t* handle, uv_os_fd_t* fd);
+
+UV_EXTERN uv_buf_t uv_buf_init(char* base, unsigned int len);
+
+UV_EXTERN int uv_pipe(uv_file fds[2], int read_flags, int write_flags);
+UV_EXTERN int uv_socketpair(int type,
+                            int protocol,
+                            uv_os_sock_t socket_vector[2],
+                            int flags0,
+                            int flags1);
+
+#define UV_STREAM_FIELDS                                                      \
+  /* number of bytes queued for writing */                                    \
+  size_t write_queue_size;                                                    \
+  uv_alloc_cb alloc_cb;                                                       \
+  uv_read_cb read_cb;                                                         \
+  /* private */                                                               \
+  UV_STREAM_PRIVATE_FIELDS
+
+/*
+ * uv_stream_t is a subclass of uv_handle_t.
+ *
+ * uv_stream is an abstract class.
+ *
+ * uv_stream_t is the parent class of uv_tcp_t, uv_pipe_t and uv_tty_t.
+ */
+struct uv_stream_s {
+  UV_HANDLE_FIELDS
+  UV_STREAM_FIELDS
+};
+
+UV_EXTERN size_t uv_stream_get_write_queue_size(const uv_stream_t* stream);
+
+UV_EXTERN int uv_listen(uv_stream_t* stream, int backlog, uv_connection_cb cb);
+UV_EXTERN int uv_accept(uv_stream_t* server, uv_stream_t* client);
+
+UV_EXTERN int uv_read_start(uv_stream_t*,
+                            uv_alloc_cb alloc_cb,
+                            uv_read_cb read_cb);
+UV_EXTERN int uv_read_stop(uv_stream_t*);
+
+UV_EXTERN int uv_write(uv_write_t* req,
+                       uv_stream_t* handle,
+                       const uv_buf_t bufs[],
+                       unsigned int nbufs,
+                       uv_write_cb cb);
+UV_EXTERN int uv_write2(uv_write_t* req,
+                        uv_stream_t* handle,
+                        const uv_buf_t bufs[],
+                        unsigned int nbufs,
+                        uv_stream_t* send_handle,
+                        uv_write_cb cb);
+UV_EXTERN int uv_try_write(uv_stream_t* handle,
+                           const uv_buf_t bufs[],
+                           unsigned int nbufs);
+UV_EXTERN int uv_try_write2(uv_stream_t* handle,
+                            const uv_buf_t bufs[],
+                            unsigned int nbufs,
+                            uv_stream_t* send_handle);
+
+/* uv_write_t is a subclass of uv_req_t. */
+struct uv_write_s {
+  UV_REQ_FIELDS
+  uv_write_cb cb;
+  uv_stream_t* send_handle; /* TODO: make private and unix-only in v2.x. */
+  uv_stream_t* handle;
+  UV_WRITE_PRIVATE_FIELDS
+};
+
+
+UV_EXTERN int uv_is_readable(const uv_stream_t* handle);
+UV_EXTERN int uv_is_writable(const uv_stream_t* handle);
+
+UV_EXTERN int uv_stream_set_blocking(uv_stream_t* handle, int blocking);
+
+UV_EXTERN int uv_is_closing(const uv_handle_t* handle);
+
+
+/*
+ * uv_tcp_t is a subclass of uv_stream_t.
+ *
+ * Represents a TCP stream or TCP server.
+ */
+struct uv_tcp_s {
+  UV_HANDLE_FIELDS
+  UV_STREAM_FIELDS
+  UV_TCP_PRIVATE_FIELDS
+};
+
+UV_EXTERN int uv_tcp_init(uv_loop_t*, uv_tcp_t* handle);
+UV_EXTERN int uv_tcp_init_ex(uv_loop_t*, uv_tcp_t* handle, unsigned int flags);
+UV_EXTERN int uv_tcp_open(uv_tcp_t* handle, uv_os_sock_t sock);
+UV_EXTERN int uv_tcp_nodelay(uv_tcp_t* handle, int enable);
+UV_EXTERN int uv_tcp_keepalive(uv_tcp_t* handle,
+                               int enable,
+                               unsigned int delay);
+UV_EXTERN int uv_tcp_simultaneous_accepts(uv_tcp_t* handle, int enable);
+
+enum uv_tcp_flags {
+  /* Used with uv_tcp_bind, when an IPv6 address is used. */
+  UV_TCP_IPV6ONLY = 1
+};
+
+UV_EXTERN int uv_tcp_bind(uv_tcp_t* handle,
+                          const struct sockaddr* addr,
+                          unsigned int flags);
+UV_EXTERN int uv_tcp_getsockname(const uv_tcp_t* handle,
+                                 struct sockaddr* name,
+                                 int* namelen);
+UV_EXTERN int uv_tcp_getpeername(const uv_tcp_t* handle,
+                                 struct sockaddr* name,
+                                 int* namelen);
+UV_EXTERN int uv_tcp_close_reset(uv_tcp_t* handle, uv_close_cb close_cb);
+UV_EXTERN int uv_tcp_connect(uv_connect_t* req,
+                             uv_tcp_t* handle,
+                             const struct sockaddr* addr,
+                             uv_connect_cb cb);
+
+/* uv_connect_t is a subclass of uv_req_t. */
+struct uv_connect_s {
+  UV_REQ_FIELDS
+  uv_connect_cb cb;
+  uv_stream_t* handle;
+  UV_CONNECT_PRIVATE_FIELDS
+};
+
+
+/*
+ * UDP support.
+ */
+
+enum uv_udp_flags {
+  /* Disables dual stack mode. */
+  UV_UDP_IPV6ONLY = 1,
+  /*
+   * Indicates message was truncated because read buffer was too small. The
+   * remainder was discarded by the OS. Used in uv_udp_recv_cb.
+   */
+  UV_UDP_PARTIAL = 2,
+  /*
+   * Indicates if SO_REUSEADDR will be set when binding the handle.
+   * This sets the SO_REUSEPORT socket flag on the BSDs and OS X. On other
+   * Unix platforms, it sets the SO_REUSEADDR flag.  What that means is that
+   * multiple threads or processes can bind to the same address without error
+   * (provided they all set the flag) but only the last one to bind will receive
+   * any traffic, in effect "stealing" the port from the previous listener.
+   */
+  UV_UDP_REUSEADDR = 4,
+  /*
+   * Indicates that the message was received by recvmmsg, so the buffer provided
+   * must not be freed by the recv_cb callback.
+   */
+  UV_UDP_MMSG_CHUNK = 8,
+  /*
+   * Indicates that the buffer provided has been fully utilized by recvmmsg and
+   * that it should now be freed by the recv_cb callback. When this flag is set
+   * in uv_udp_recv_cb, nread will always be 0 and addr will always be NULL.
+   */
+  UV_UDP_MMSG_FREE = 16,
+  /*
+   * Indicates if IP_RECVERR/IPV6_RECVERR will be set when binding the handle.
+   * This sets IP_RECVERR for IPv4 and IPV6_RECVERR for IPv6 UDP sockets on
+   * Linux. This stops the Linux kernel from suppressing some ICMP error
+   * messages and enables full ICMP error reporting for faster failover.
+   * This flag is no-op on platforms other than Linux.
+   */
+  UV_UDP_LINUX_RECVERR = 32,
+  /*
+   * Indicates that recvmmsg should be used, if available.
+   */
+  UV_UDP_RECVMMSG = 256
+};
+
+typedef void (*uv_udp_send_cb)(uv_udp_send_t* req, int status);
+typedef void (*uv_udp_recv_cb)(uv_udp_t* handle,
+                               ssize_t nread,
+                               const uv_buf_t* buf,
+                               const struct sockaddr* addr,
+                               unsigned flags);
+
+/* uv_udp_t is a subclass of uv_handle_t. */
+struct uv_udp_s {
+  UV_HANDLE_FIELDS
+  /* read-only */
+  /*
+   * Number of bytes queued for sending. This field strictly shows how much
+   * information is currently queued.
+   */
+  size_t send_queue_size;
+  /*
+   * Number of send requests currently in the queue awaiting to be processed.
+   */
+  size_t send_queue_count;
+  UV_UDP_PRIVATE_FIELDS
+};
+
+/* uv_udp_send_t is a subclass of uv_req_t. */
+struct uv_udp_send_s {
+  UV_REQ_FIELDS
+  uv_udp_t* handle;
+  uv_udp_send_cb cb;
+  UV_UDP_SEND_PRIVATE_FIELDS
+};
+
+UV_EXTERN int uv_udp_init(uv_loop_t*, uv_udp_t* handle);
+UV_EXTERN int uv_udp_init_ex(uv_loop_t*, uv_udp_t* handle, unsigned int flags);
+UV_EXTERN int uv_udp_open(uv_udp_t* handle, uv_os_sock_t sock);
+UV_EXTERN int uv_udp_bind(uv_udp_t* handle,
+                          const struct sockaddr* addr,
+                          unsigned int flags);
+UV_EXTERN int uv_udp_connect(uv_udp_t* handle, const struct sockaddr* addr);
+
+UV_EXTERN int uv_udp_getpeername(const uv_udp_t* handle,
+                                 struct sockaddr* name,
+                                 int* namelen);
+UV_EXTERN int uv_udp_getsockname(const uv_udp_t* handle,
+                                 struct sockaddr* name,
+                                 int* namelen);
+UV_EXTERN int uv_udp_set_membership(uv_udp_t* handle,
+                                    const char* multicast_addr,
+                                    const char* interface_addr,
+                                    uv_membership membership);
+UV_EXTERN int uv_udp_set_source_membership(uv_udp_t* handle,
+                                           const char* multicast_addr,
+                                           const char* interface_addr,
+                                           const char* source_addr,
+                                           uv_membership membership);
+UV_EXTERN int uv_udp_set_multicast_loop(uv_udp_t* handle, int on);
+UV_EXTERN int uv_udp_set_multicast_ttl(uv_udp_t* handle, int ttl);
+UV_EXTERN int uv_udp_set_multicast_interface(uv_udp_t* handle,
+                                             const char* interface_addr);
+UV_EXTERN int uv_udp_set_broadcast(uv_udp_t* handle, int on);
+UV_EXTERN int uv_udp_set_ttl(uv_udp_t* handle, int ttl);
+UV_EXTERN int uv_udp_send(uv_udp_send_t* req,
+                          uv_udp_t* handle,
+                          const uv_buf_t bufs[],
+                          unsigned int nbufs,
+                          const struct sockaddr* addr,
+                          uv_udp_send_cb send_cb);
+UV_EXTERN int uv_udp_try_send(uv_udp_t* handle,
+                              const uv_buf_t bufs[],
+                              unsigned int nbufs,
+                              const struct sockaddr* addr);
+UV_EXTERN int uv_udp_recv_start(uv_udp_t* handle,
+                                uv_alloc_cb alloc_cb,
+                                uv_udp_recv_cb recv_cb);
+UV_EXTERN int uv_udp_using_recvmmsg(const uv_udp_t* handle);
+UV_EXTERN int uv_udp_recv_stop(uv_udp_t* handle);
+UV_EXTERN size_t uv_udp_get_send_queue_size(const uv_udp_t* handle);
+UV_EXTERN size_t uv_udp_get_send_queue_count(const uv_udp_t* handle);
+
+
+/*
+ * uv_tty_t is a subclass of uv_stream_t.
+ *
+ * Representing a stream for the console.
+ */
+struct uv_tty_s {
+  UV_HANDLE_FIELDS
+  UV_STREAM_FIELDS
+  UV_TTY_PRIVATE_FIELDS
+};
+
+typedef enum {
+  /* Initial/normal terminal mode */
+  UV_TTY_MODE_NORMAL,
+  /* Raw input mode (On Windows, ENABLE_WINDOW_INPUT is also enabled) */
+  UV_TTY_MODE_RAW,
+  /* Binary-safe I/O mode for IPC (Unix-only) */
+  UV_TTY_MODE_IO
+} uv_tty_mode_t;
+
+typedef enum {
+  /*
+   * The console supports handling of virtual terminal sequences
+   * (Windows10 new console, ConEmu)
+   */
+  UV_TTY_SUPPORTED,
+  /* The console cannot process the virtual terminal sequence.  (Legacy
+   * console)
+   */
+  UV_TTY_UNSUPPORTED
+} uv_tty_vtermstate_t;
+
+
+UV_EXTERN int uv_tty_init(uv_loop_t*, uv_tty_t*, uv_file fd, int readable);
+UV_EXTERN int uv_tty_set_mode(uv_tty_t*, uv_tty_mode_t mode);
+UV_EXTERN int uv_tty_reset_mode(void);
+UV_EXTERN int uv_tty_get_winsize(uv_tty_t*, int* width, int* height);
+UV_EXTERN void uv_tty_set_vterm_state(uv_tty_vtermstate_t state);
+UV_EXTERN int uv_tty_get_vterm_state(uv_tty_vtermstate_t* state);
+
+inline int uv_tty_set_mode(uv_tty_t* handle, int mode) {
+  return uv_tty_set_mode(handle, static_cast<uv_tty_mode_t>(mode));
+}
+
+UV_EXTERN uv_handle_type uv_guess_handle(uv_file file);
+
+/*
+ * uv_pipe_t is a subclass of uv_stream_t.
+ *
+ * Representing a pipe stream or pipe server. On Windows this is a Named
+ * Pipe. On Unix this is a Unix domain socket.
+ */
+struct uv_pipe_s {
+  UV_HANDLE_FIELDS
+  UV_STREAM_FIELDS
+  int ipc; /* non-zero if this pipe is used for passing handles */
+  UV_PIPE_PRIVATE_FIELDS
+};
+
+UV_EXTERN int uv_pipe_init(uv_loop_t*, uv_pipe_t* handle, int ipc);
+UV_EXTERN int uv_pipe_open(uv_pipe_t*, uv_file file);
+UV_EXTERN int uv_pipe_bind(uv_pipe_t* handle, const char* name);
+UV_EXTERN void uv_pipe_connect(uv_connect_t* req,
+                               uv_pipe_t* handle,
+                               const char* name,
+                               uv_connect_cb cb);
+UV_EXTERN int uv_pipe_getsockname(const uv_pipe_t* handle,
+                                  char* buffer,
+                                  size_t* size);
+UV_EXTERN int uv_pipe_getpeername(const uv_pipe_t* handle,
+                                  char* buffer,
+                                  size_t* size);
+UV_EXTERN void uv_pipe_pending_instances(uv_pipe_t* handle, int count);
+UV_EXTERN int uv_pipe_pending_count(uv_pipe_t* handle);
+UV_EXTERN uv_handle_type uv_pipe_pending_type(uv_pipe_t* handle);
+UV_EXTERN int uv_pipe_chmod(uv_pipe_t* handle, int flags);
+
+
+struct uv_poll_s {
+  UV_HANDLE_FIELDS
+  uv_poll_cb poll_cb;
+  UV_POLL_PRIVATE_FIELDS
+};
+
+enum uv_poll_event {
+  UV_READABLE = 1,
+  UV_WRITABLE = 2,
+  UV_DISCONNECT = 4,
+  UV_PRIORITIZED = 8
+};
+
+UV_EXTERN int uv_poll_init(uv_loop_t* loop, uv_poll_t* handle, int fd);
+UV_EXTERN int uv_poll_init_socket(uv_loop_t* loop,
+                                  uv_poll_t* handle,
+                                  uv_os_sock_t socket);
+UV_EXTERN int uv_poll_start(uv_poll_t* handle, int events, uv_poll_cb cb);
+UV_EXTERN int uv_poll_stop(uv_poll_t* handle);
+
+
+struct uv_prepare_s {
+  UV_HANDLE_FIELDS
+  UV_PREPARE_PRIVATE_FIELDS
+};
+
+UV_EXTERN int uv_prepare_init(uv_loop_t*, uv_prepare_t* prepare);
+UV_EXTERN int uv_prepare_start(uv_prepare_t* prepare, uv_prepare_cb cb);
+UV_EXTERN int uv_prepare_stop(uv_prepare_t* prepare);
+
+
+struct uv_check_s {
+  UV_HANDLE_FIELDS
+  UV_CHECK_PRIVATE_FIELDS
+};
+
+UV_EXTERN int uv_check_init(uv_loop_t*, uv_check_t* check);
+UV_EXTERN int uv_check_start(uv_check_t* check, uv_check_cb cb);
+UV_EXTERN int uv_check_stop(uv_check_t* check);
+
+
+struct uv_idle_s {
+  UV_HANDLE_FIELDS
+  UV_IDLE_PRIVATE_FIELDS
+};
+
+UV_EXTERN int uv_idle_init(uv_loop_t*, uv_idle_t* idle);
+UV_EXTERN int uv_idle_start(uv_idle_t* idle, uv_idle_cb cb);
+UV_EXTERN int uv_idle_stop(uv_idle_t* idle);
+
+
+struct uv_async_s {
+  UV_HANDLE_FIELDS
+  UV_ASYNC_PRIVATE_FIELDS
+};
+
+UV_EXTERN int uv_async_init(uv_loop_t*,
+                            uv_async_t* async,
+                            uv_async_cb async_cb);
+UV_EXTERN int uv_async_send(uv_async_t* async);
+
+
+/*
+ * uv_timer_t is a subclass of uv_handle_t.
+ *
+ * Used to get woken up at a specified time in the future.
+ */
+struct uv_timer_s {
+  UV_HANDLE_FIELDS
+  UV_TIMER_PRIVATE_FIELDS
+};
+
+UV_EXTERN int uv_timer_init(uv_loop_t*, uv_timer_t* handle);
+UV_EXTERN int uv_timer_start(uv_timer_t* handle,
+                             uv_timer_cb cb,
+                             uint64_t timeout,
+                             uint64_t repeat);
+UV_EXTERN int uv_timer_stop(uv_timer_t* handle);
+UV_EXTERN int uv_timer_again(uv_timer_t* handle);
+UV_EXTERN void uv_timer_set_repeat(uv_timer_t* handle, uint64_t repeat);
+UV_EXTERN uint64_t uv_timer_get_repeat(const uv_timer_t* handle);
+UV_EXTERN uint64_t uv_timer_get_due_in(const uv_timer_t* handle);
+
+
+/*
+ * uv_getaddrinfo_t is a subclass of uv_req_t.
+ *
+ * Request object for uv_getaddrinfo.
+ */
+struct uv_getaddrinfo_s {
+  UV_REQ_FIELDS
+  /* read-only */
+  uv_loop_t* loop;
+  /* struct addrinfo* addrinfo is marked as private, but it really isn't. */
+  UV_GETADDRINFO_PRIVATE_FIELDS
+};
+
+
+UV_EXTERN int uv_getaddrinfo(uv_loop_t* loop,
+                             uv_getaddrinfo_t* req,
+                             uv_getaddrinfo_cb getaddrinfo_cb,
+                             const char* node,
+                             const char* service,
+                             const struct addrinfo* hints);
+UV_EXTERN void uv_freeaddrinfo(struct addrinfo* ai);
+
+
+/*
+* uv_getnameinfo_t is a subclass of uv_req_t.
+*
+* Request object for uv_getnameinfo.
+*/
+struct uv_getnameinfo_s {
+  UV_REQ_FIELDS
+  /* read-only */
+  uv_loop_t* loop;
+  /* host and service are marked as private, but they really aren't. */
+  UV_GETNAMEINFO_PRIVATE_FIELDS
+};
+
+UV_EXTERN int uv_getnameinfo(uv_loop_t* loop,
+                             uv_getnameinfo_t* req,
+                             uv_getnameinfo_cb getnameinfo_cb,
+                             const struct sockaddr* addr,
+                             int flags);
+
+
+/* uv_spawn() options. */
+typedef enum {
+  UV_IGNORE         = 0x00,
+  UV_CREATE_PIPE    = 0x01,
+  UV_INHERIT_FD     = 0x02,
+  UV_INHERIT_STREAM = 0x04,
+
+  /*
+   * When UV_CREATE_PIPE is specified, UV_READABLE_PIPE and UV_WRITABLE_PIPE
+   * determine the direction of flow, from the child process' perspective. Both
+   * flags may be specified to create a duplex data stream.
+   */
+  UV_READABLE_PIPE  = 0x10,
+  UV_WRITABLE_PIPE  = 0x20,
+
+  /*
+   * When UV_CREATE_PIPE is specified, specifying UV_NONBLOCK_PIPE opens the
+   * handle in non-blocking mode in the child. This may cause loss of data,
+   * if the child is not designed to handle to encounter this mode,
+   * but can also be significantly more efficient.
+   */
+  UV_NONBLOCK_PIPE  = 0x40,
+  UV_OVERLAPPED_PIPE = 0x40 /* old name, for compatibility */
+} uv_stdio_flags;
+
+typedef struct uv_stdio_container_s {
+  uv_stdio_flags flags;
+
+  union {
+    uv_stream_t* stream;
+    int fd;
+  } data;
+} uv_stdio_container_t;
+
+typedef struct uv_process_options_s {
+  uv_exit_cb exit_cb; /* Called after the process exits. */
+  const char* file;   /* Path to program to execute. */
+  /*
+   * Command line arguments. args[0] should be the path to the program. On
+   * Windows this uses CreateProcess which concatenates the arguments into a
+   * string this can cause some strange errors. See the note at
+   * windows_verbatim_arguments.
+   */
+  char** args;
+  /*
+   * This will be set as the environ variable in the subprocess. If this is
+   * NULL then the parents environ will be used.
+   */
+  char** env;
+  /*
+   * If non-null this represents a directory the subprocess should execute
+   * in. Stands for current working directory.
+   */
+  const char* cwd;
+  /*
+   * Various flags that control how uv_spawn() behaves. See the definition of
+   * `enum uv_process_flags` below.
+   */
+  unsigned int flags;
+  /*
+   * The `stdio` field points to an array of uv_stdio_container_t structs that
+   * describe the file descriptors that will be made available to the child
+   * process. The convention is that stdio[0] points to stdin, fd 1 is used for
+   * stdout, and fd 2 is stderr.
+   *
+   * Note that on windows file descriptors greater than 2 are available to the
+   * child process only if the child processes uses the MSVCRT runtime.
+   */
+  int stdio_count;
+  uv_stdio_container_t* stdio;
+  /*
+   * Libuv can change the child process' user/group id. This happens only when
+   * the appropriate bits are set in the flags fields. This is not supported on
+   * windows; uv_spawn() will fail and set the error to UV_ENOTSUP.
+   */
+  uv_uid_t uid;
+  uv_gid_t gid;
+} uv_process_options_t;
+
+/*
+ * These are the flags that can be used for the uv_process_options.flags field.
+ */
+enum uv_process_flags {
+  /*
+   * Set the child process' user id. The user id is supplied in the `uid` field
+   * of the options struct. This does not work on windows; setting this flag
+   * will cause uv_spawn() to fail.
+   */
+  UV_PROCESS_SETUID = (1 << 0),
+  /*
+   * Set the child process' group id. The user id is supplied in the `gid`
+   * field of the options struct. This does not work on windows; setting this
+   * flag will cause uv_spawn() to fail.
+   */
+  UV_PROCESS_SETGID = (1 << 1),
+  /*
+   * Do not wrap any arguments in quotes, or perform any other escaping, when
+   * converting the argument list into a command line string. This option is
+   * only meaningful on Windows systems. On Unix it is silently ignored.
+   */
+  UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS = (1 << 2),
+  /*
+   * Spawn the child process in a detached state - this will make it a process
+   * group leader, and will effectively enable the child to keep running after
+   * the parent exits.  Note that the child process will still keep the
+   * parent's event loop alive unless the parent process calls uv_unref() on
+   * the child's process handle.
+   */
+  UV_PROCESS_DETACHED = (1 << 3),
+  /*
+   * Hide the subprocess window that would normally be created. This option is
+   * only meaningful on Windows systems. On Unix it is silently ignored.
+   */
+  UV_PROCESS_WINDOWS_HIDE = (1 << 4),
+  /*
+   * Hide the subprocess console window that would normally be created. This
+   * option is only meaningful on Windows systems. On Unix it is silently
+   * ignored.
+   */
+  UV_PROCESS_WINDOWS_HIDE_CONSOLE = (1 << 5),
+  /*
+   * Hide the subprocess GUI window that would normally be created. This
+   * option is only meaningful on Windows systems. On Unix it is silently
+   * ignored.
+   */
+  UV_PROCESS_WINDOWS_HIDE_GUI = (1 << 6)
+};
+
+/*
+ * uv_process_t is a subclass of uv_handle_t.
+ */
+struct uv_process_s {
+  UV_HANDLE_FIELDS
+  uv_exit_cb exit_cb;
+  int pid;
+  UV_PROCESS_PRIVATE_FIELDS
+};
+
+UV_EXTERN int uv_spawn(uv_loop_t* loop,
+                       uv_process_t* handle,
+                       const uv_process_options_t* options);
+UV_EXTERN int uv_process_kill(uv_process_t*, int signum);
+UV_EXTERN int uv_kill(int pid, int signum);
+UV_EXTERN uv_pid_t uv_process_get_pid(const uv_process_t*);
+
+
+/*
+ * uv_work_t is a subclass of uv_req_t.
+ */
+struct uv_work_s {
+  UV_REQ_FIELDS
+  uv_loop_t* loop;
+  uv_work_cb work_cb;
+  uv_after_work_cb after_work_cb;
+  UV_WORK_PRIVATE_FIELDS
+};
+
+UV_EXTERN int uv_queue_work(uv_loop_t* loop,
+                            uv_work_t* req,
+                            uv_work_cb work_cb,
+                            uv_after_work_cb after_work_cb);
+
+UV_EXTERN int uv_cancel(uv_req_t* req);
+
+
+struct uv_cpu_times_s {
+  uint64_t user; /* milliseconds */
+  uint64_t nice; /* milliseconds */
+  uint64_t sys; /* milliseconds */
+  uint64_t idle; /* milliseconds */
+  uint64_t irq; /* milliseconds */
+};
+
+struct uv_cpu_info_s {
+  char* model;
+  int speed;
+  struct uv_cpu_times_s cpu_times;
+};
+
+struct uv_interface_address_s {
+  char* name;
+  char phys_addr[6];
+  int is_internal;
+  union {
+    struct sockaddr_in address4;
+    struct sockaddr_in6 address6;
+  } address;
+  union {
+    struct sockaddr_in netmask4;
+    struct sockaddr_in6 netmask6;
+  } netmask;
+};
+
+struct uv_passwd_s {
+  char* username;
+  unsigned long uid;
+  unsigned long gid;
+  char* shell;
+  char* homedir;
+};
+
+struct uv_utsname_s {
+  char sysname[256];
+  char release[256];
+  char version[256];
+  char machine[256];
+  /* This struct does not contain the nodename and domainname fields present in
+     the utsname type. domainname is a GNU extension. Both fields are referred
+     to as meaningless in the docs. */
+};
+
+struct uv_statfs_s {
+  uint64_t f_type;
+  uint64_t f_bsize;
+  uint64_t f_blocks;
+  uint64_t f_bfree;
+  uint64_t f_bavail;
+  uint64_t f_files;
+  uint64_t f_ffree;
+  uint64_t f_spare[4];
+};
+
+typedef enum {
+  UV_DIRENT_UNKNOWN,
+  UV_DIRENT_FILE,
+  UV_DIRENT_DIR,
+  UV_DIRENT_LINK,
+  UV_DIRENT_FIFO,
+  UV_DIRENT_SOCKET,
+  UV_DIRENT_CHAR,
+  UV_DIRENT_BLOCK
+} uv_dirent_type_t;
+
+struct uv_dirent_s {
+  const char* name;
+  uv_dirent_type_t type;
+};
+
+UV_EXTERN char** uv_setup_args(int argc, char** argv);
+UV_EXTERN int uv_get_process_title(char* buffer, size_t size);
+UV_EXTERN int uv_set_process_title(const char* title);
+UV_EXTERN int uv_resident_set_memory(size_t* rss);
+UV_EXTERN int uv_uptime(double* uptime);
+UV_EXTERN uv_os_fd_t uv_get_osfhandle(int fd);
+UV_EXTERN int uv_open_osfhandle(uv_os_fd_t os_fd);
+
+typedef struct {
+  long tv_sec;
+  long tv_usec;
+} uv_timeval_t;
+
+typedef struct {
+  int64_t tv_sec;
+  int32_t tv_usec;
+} uv_timeval64_t;
+
+typedef struct {
+   uv_timeval_t ru_utime; /* user CPU time used */
+   uv_timeval_t ru_stime; /* system CPU time used */
+   uint64_t ru_maxrss;    /* maximum resident set size */
+   uint64_t ru_ixrss;     /* integral shared memory size */
+   uint64_t ru_idrss;     /* integral unshared data size */
+   uint64_t ru_isrss;     /* integral unshared stack size */
+   uint64_t ru_minflt;    /* page reclaims (soft page faults) */
+   uint64_t ru_majflt;    /* page faults (hard page faults) */
+   uint64_t ru_nswap;     /* swaps */
+   uint64_t ru_inblock;   /* block input operations */
+   uint64_t ru_oublock;   /* block output operations */
+   uint64_t ru_msgsnd;    /* IPC messages sent */
+   uint64_t ru_msgrcv;    /* IPC messages received */
+   uint64_t ru_nsignals;  /* signals received */
+   uint64_t ru_nvcsw;     /* voluntary context switches */
+   uint64_t ru_nivcsw;    /* involuntary context switches */
+} uv_rusage_t;
+
+UV_EXTERN int uv_getrusage(uv_rusage_t* rusage);
+
+UV_EXTERN int uv_os_homedir(char* buffer, size_t* size);
+UV_EXTERN int uv_os_tmpdir(char* buffer, size_t* size);
+UV_EXTERN int uv_os_get_passwd(uv_passwd_t* pwd);
+UV_EXTERN void uv_os_free_passwd(uv_passwd_t* pwd);
+UV_EXTERN uv_pid_t uv_os_getpid(void);
+UV_EXTERN uv_pid_t uv_os_getppid(void);
+
+#if defined(__PASE__)
+/* On IBM i PASE, the highest process priority is -10 */
+# define UV_PRIORITY_LOW 39          /* RUNPTY(99) */
+# define UV_PRIORITY_BELOW_NORMAL 15 /* RUNPTY(50) */
+# define UV_PRIORITY_NORMAL 0        /* RUNPTY(20) */
+# define UV_PRIORITY_ABOVE_NORMAL -4 /* RUNTY(12) */
+# define UV_PRIORITY_HIGH -7         /* RUNPTY(6) */
+# define UV_PRIORITY_HIGHEST -10     /* RUNPTY(1) */
+#else
+# define UV_PRIORITY_LOW 19
+# define UV_PRIORITY_BELOW_NORMAL 10
+# define UV_PRIORITY_NORMAL 0
+# define UV_PRIORITY_ABOVE_NORMAL -7
+# define UV_PRIORITY_HIGH -14
+# define UV_PRIORITY_HIGHEST -20
+#endif
+
+UV_EXTERN int uv_os_getpriority(uv_pid_t pid, int* priority);
+UV_EXTERN int uv_os_setpriority(uv_pid_t pid, int priority);
+
+UV_EXTERN unsigned int uv_available_parallelism(void);
+UV_EXTERN int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count);
+UV_EXTERN void uv_free_cpu_info(uv_cpu_info_t* cpu_infos, int count);
+
+UV_EXTERN int uv_interface_addresses(uv_interface_address_t** addresses,
+                                     int* count);
+UV_EXTERN void uv_free_interface_addresses(uv_interface_address_t* addresses,
+                                           int count);
+
+struct uv_env_item_s {
+  char* name;
+  char* value;
+};
+
+UV_EXTERN int uv_os_environ(uv_env_item_t** envitems, int* count);
+UV_EXTERN void uv_os_free_environ(uv_env_item_t* envitems, int count);
+UV_EXTERN int uv_os_getenv(const char* name, char* buffer, size_t* size);
+UV_EXTERN int uv_os_setenv(const char* name, const char* value);
+UV_EXTERN int uv_os_unsetenv(const char* name);
+
+#ifdef MAXHOSTNAMELEN
+# define UV_MAXHOSTNAMESIZE (MAXHOSTNAMELEN + 1)
+#else
+  /*
+    Fallback for the maximum hostname size, including the null terminator. The
+    Windows gethostname() documentation states that 256 bytes will always be
+    large enough to hold the null-terminated hostname.
+  */
+# define UV_MAXHOSTNAMESIZE 256
+#endif
+
+UV_EXTERN int uv_os_gethostname(char* buffer, size_t* size);
+
+UV_EXTERN int uv_os_uname(uv_utsname_t* buffer);
+
+UV_EXTERN uint64_t uv_metrics_idle_time(uv_loop_t* loop);
+
+typedef enum {
+  UV_FS_UNKNOWN = -1,
+  UV_FS_CUSTOM,
+  UV_FS_OPEN,
+  UV_FS_CLOSE,
+  UV_FS_READ,
+  UV_FS_WRITE,
+  UV_FS_SENDFILE,
+  UV_FS_STAT,
+  UV_FS_LSTAT,
+  UV_FS_FSTAT,
+  UV_FS_FTRUNCATE,
+  UV_FS_UTIME,
+  UV_FS_FUTIME,
+  UV_FS_ACCESS,
+  UV_FS_CHMOD,
+  UV_FS_FCHMOD,
+  UV_FS_FSYNC,
+  UV_FS_FDATASYNC,
+  UV_FS_UNLINK,
+  UV_FS_RMDIR,
+  UV_FS_MKDIR,
+  UV_FS_MKDTEMP,
+  UV_FS_RENAME,
+  UV_FS_SCANDIR,
+  UV_FS_LINK,
+  UV_FS_SYMLINK,
+  UV_FS_READLINK,
+  UV_FS_CHOWN,
+  UV_FS_FCHOWN,
+  UV_FS_REALPATH,
+  UV_FS_COPYFILE,
+  UV_FS_LCHOWN,
+  UV_FS_OPENDIR,
+  UV_FS_READDIR,
+  UV_FS_CLOSEDIR,
+  UV_FS_STATFS,
+  UV_FS_MKSTEMP,
+  UV_FS_LUTIME
+} uv_fs_type;
+
+struct uv_dir_s {
+  uv_dirent_t* dirents;
+  size_t nentries;
+  void* reserved[4];
+  UV_DIR_PRIVATE_FIELDS
+};
+
+/* uv_fs_t is a subclass of uv_req_t. */
+struct uv_fs_s {
+  UV_REQ_FIELDS
+  uv_fs_type fs_type;
+  uv_loop_t* loop;
+  uv_fs_cb cb;
+  ssize_t result;
+  void* ptr;
+  const char* path;
+  uv_stat_t statbuf;  /* Stores the result of uv_fs_stat() and uv_fs_fstat(). */
+  UV_FS_PRIVATE_FIELDS
+};
+
+UV_EXTERN uv_fs_type uv_fs_get_type(const uv_fs_t*);
+UV_EXTERN ssize_t uv_fs_get_result(const uv_fs_t*);
+UV_EXTERN int uv_fs_get_system_error(const uv_fs_t*);
+UV_EXTERN void* uv_fs_get_ptr(const uv_fs_t*);
+UV_EXTERN const char* uv_fs_get_path(const uv_fs_t*);
+UV_EXTERN uv_stat_t* uv_fs_get_statbuf(uv_fs_t*);
+
+UV_EXTERN void uv_fs_req_cleanup(uv_fs_t* req);
+UV_EXTERN int uv_fs_close(uv_loop_t* loop,
+                          uv_fs_t* req,
+                          uv_file file,
+                          uv_fs_cb cb);
+UV_EXTERN int uv_fs_open(uv_loop_t* loop,
+                         uv_fs_t* req,
+                         const char* path,
+                         int flags,
+                         int mode,
+                         uv_fs_cb cb);
+UV_EXTERN int uv_fs_read(uv_loop_t* loop,
+                         uv_fs_t* req,
+                         uv_file file,
+                         const uv_buf_t bufs[],
+                         unsigned int nbufs,
+                         int64_t offset,
+                         uv_fs_cb cb);
+UV_EXTERN int uv_fs_unlink(uv_loop_t* loop,
+                           uv_fs_t* req,
+                           const char* path,
+                           uv_fs_cb cb);
+UV_EXTERN int uv_fs_write(uv_loop_t* loop,
+                          uv_fs_t* req,
+                          uv_file file,
+                          const uv_buf_t bufs[],
+                          unsigned int nbufs,
+                          int64_t offset,
+                          uv_fs_cb cb);
+/*
+ * This flag can be used with uv_fs_copyfile() to return an error if the
+ * destination already exists.
+ */
+#define UV_FS_COPYFILE_EXCL   0x0001
+
+/*
+ * This flag can be used with uv_fs_copyfile() to attempt to create a reflink.
+ * If copy-on-write is not supported, a fallback copy mechanism is used.
+ */
+#define UV_FS_COPYFILE_FICLONE 0x0002
+
+/*
+ * This flag can be used with uv_fs_copyfile() to attempt to create a reflink.
+ * If copy-on-write is not supported, an error is returned.
+ */
+#define UV_FS_COPYFILE_FICLONE_FORCE 0x0004
+
+UV_EXTERN int uv_fs_copyfile(uv_loop_t* loop,
+                             uv_fs_t* req,
+                             const char* path,
+                             const char* new_path,
+                             int flags,
+                             uv_fs_cb cb);
+UV_EXTERN int uv_fs_mkdir(uv_loop_t* loop,
+                          uv_fs_t* req,
+                          const char* path,
+                          int mode,
+                          uv_fs_cb cb);
+UV_EXTERN int uv_fs_mkdtemp(uv_loop_t* loop,
+                            uv_fs_t* req,
+                            const char* tpl,
+                            uv_fs_cb cb);
+UV_EXTERN int uv_fs_mkstemp(uv_loop_t* loop,
+                            uv_fs_t* req,
+                            const char* tpl,
+                            uv_fs_cb cb);
+UV_EXTERN int uv_fs_rmdir(uv_loop_t* loop,
+                          uv_fs_t* req,
+                          const char* path,
+                          uv_fs_cb cb);
+UV_EXTERN int uv_fs_scandir(uv_loop_t* loop,
+                            uv_fs_t* req,
+                            const char* path,
+                            int flags,
+                            uv_fs_cb cb);
+UV_EXTERN int uv_fs_scandir_next(uv_fs_t* req,
+                                 uv_dirent_t* ent);
+UV_EXTERN int uv_fs_opendir(uv_loop_t* loop,
+                            uv_fs_t* req,
+                            const char* path,
+                            uv_fs_cb cb);
+UV_EXTERN int uv_fs_readdir(uv_loop_t* loop,
+                            uv_fs_t* req,
+                            uv_dir_t* dir,
+                            uv_fs_cb cb);
+UV_EXTERN int uv_fs_closedir(uv_loop_t* loop,
+                             uv_fs_t* req,
+                             uv_dir_t* dir,
+                             uv_fs_cb cb);
+UV_EXTERN int uv_fs_stat(uv_loop_t* loop,
+                         uv_fs_t* req,
+                         const char* path,
+                         uv_fs_cb cb);
+UV_EXTERN int uv_fs_fstat(uv_loop_t* loop,
+                          uv_fs_t* req,
+                          uv_file file,
+                          uv_fs_cb cb);
+UV_EXTERN int uv_fs_rename(uv_loop_t* loop,
+                           uv_fs_t* req,
+                           const char* path,
+                           const char* new_path,
+                           uv_fs_cb cb);
+UV_EXTERN int uv_fs_fsync(uv_loop_t* loop,
+                          uv_fs_t* req,
+                          uv_file file,
+                          uv_fs_cb cb);
+UV_EXTERN int uv_fs_fdatasync(uv_loop_t* loop,
+                              uv_fs_t* req,
+                              uv_file file,
+                              uv_fs_cb cb);
+UV_EXTERN int uv_fs_ftruncate(uv_loop_t* loop,
+                              uv_fs_t* req,
+                              uv_file file,
+                              int64_t offset,
+                              uv_fs_cb cb);
+UV_EXTERN int uv_fs_sendfile(uv_loop_t* loop,
+                             uv_fs_t* req,
+                             uv_file out_fd,
+                             uv_file in_fd,
+                             int64_t in_offset,
+                             size_t length,
+                             uv_fs_cb cb);
+UV_EXTERN int uv_fs_access(uv_loop_t* loop,
+                           uv_fs_t* req,
+                           const char* path,
+                           int mode,
+                           uv_fs_cb cb);
+UV_EXTERN int uv_fs_chmod(uv_loop_t* loop,
+                          uv_fs_t* req,
+                          const char* path,
+                          int mode,
+                          uv_fs_cb cb);
+UV_EXTERN int uv_fs_utime(uv_loop_t* loop,
+                          uv_fs_t* req,
+                          const char* path,
+                          double atime,
+                          double mtime,
+                          uv_fs_cb cb);
+UV_EXTERN int uv_fs_futime(uv_loop_t* loop,
+                           uv_fs_t* req,
+                           uv_file file,
+                           double atime,
+                           double mtime,
+                           uv_fs_cb cb);
+UV_EXTERN int uv_fs_lutime(uv_loop_t* loop,
+                           uv_fs_t* req,
+                           const char* path,
+                           double atime,
+                           double mtime,
+                           uv_fs_cb cb);
+UV_EXTERN int uv_fs_lstat(uv_loop_t* loop,
+                          uv_fs_t* req,
+                          const char* path,
+                          uv_fs_cb cb);
+UV_EXTERN int uv_fs_link(uv_loop_t* loop,
+                         uv_fs_t* req,
+                         const char* path,
+                         const char* new_path,
+                         uv_fs_cb cb);
+
+/*
+ * This flag can be used with uv_fs_symlink() on Windows to specify whether
+ * path argument points to a directory.
+ */
+#define UV_FS_SYMLINK_DIR          0x0001
+
+/*
+ * This flag can be used with uv_fs_symlink() on Windows to specify whether
+ * the symlink is to be created using junction points.
+ */
+#define UV_FS_SYMLINK_JUNCTION     0x0002
+
+UV_EXTERN int uv_fs_symlink(uv_loop_t* loop,
+                            uv_fs_t* req,
+                            const char* path,
+                            const char* new_path,
+                            int flags,
+                            uv_fs_cb cb);
+UV_EXTERN int uv_fs_readlink(uv_loop_t* loop,
+                             uv_fs_t* req,
+                             const char* path,
+                             uv_fs_cb cb);
+UV_EXTERN int uv_fs_realpath(uv_loop_t* loop,
+                             uv_fs_t* req,
+                             const char* path,
+                             uv_fs_cb cb);
+UV_EXTERN int uv_fs_fchmod(uv_loop_t* loop,
+                           uv_fs_t* req,
+                           uv_file file,
+                           int mode,
+                           uv_fs_cb cb);
+UV_EXTERN int uv_fs_chown(uv_loop_t* loop,
+                          uv_fs_t* req,
+                          const char* path,
+                          uv_uid_t uid,
+                          uv_gid_t gid,
+                          uv_fs_cb cb);
+UV_EXTERN int uv_fs_fchown(uv_loop_t* loop,
+                           uv_fs_t* req,
+                           uv_file file,
+                           uv_uid_t uid,
+                           uv_gid_t gid,
+                           uv_fs_cb cb);
+UV_EXTERN int uv_fs_lchown(uv_loop_t* loop,
+                           uv_fs_t* req,
+                           const char* path,
+                           uv_uid_t uid,
+                           uv_gid_t gid,
+                           uv_fs_cb cb);
+UV_EXTERN int uv_fs_statfs(uv_loop_t* loop,
+                           uv_fs_t* req,
+                           const char* path,
+                           uv_fs_cb cb);
+
+
+enum uv_fs_event {
+  UV_RENAME = 1,
+  UV_CHANGE = 2
+};
+
+
+struct uv_fs_event_s {
+  UV_HANDLE_FIELDS
+  /* private */
+  char* path;
+  UV_FS_EVENT_PRIVATE_FIELDS
+};
+
+
+/*
+ * uv_fs_stat() based polling file watcher.
+ */
+struct uv_fs_poll_s {
+  UV_HANDLE_FIELDS
+  /* Private, don't touch. */
+  void* poll_ctx;
+};
+
+UV_EXTERN int uv_fs_poll_init(uv_loop_t* loop, uv_fs_poll_t* handle);
+UV_EXTERN int uv_fs_poll_start(uv_fs_poll_t* handle,
+                               uv_fs_poll_cb poll_cb,
+                               const char* path,
+                               unsigned int interval);
+UV_EXTERN int uv_fs_poll_stop(uv_fs_poll_t* handle);
+UV_EXTERN int uv_fs_poll_getpath(uv_fs_poll_t* handle,
+                                 char* buffer,
+                                 size_t* size);
+
+
+struct uv_signal_s {
+  UV_HANDLE_FIELDS
+  uv_signal_cb signal_cb;
+  int signum;
+  UV_SIGNAL_PRIVATE_FIELDS
+};
+
+UV_EXTERN int uv_signal_init(uv_loop_t* loop, uv_signal_t* handle);
+UV_EXTERN int uv_signal_start(uv_signal_t* handle,
+                              uv_signal_cb signal_cb,
+                              int signum);
+UV_EXTERN int uv_signal_start_oneshot(uv_signal_t* handle,
+                                      uv_signal_cb signal_cb,
+                                      int signum);
+UV_EXTERN int uv_signal_stop(uv_signal_t* handle);
+
+UV_EXTERN void uv_loadavg(double avg[3]);
+
+
+/*
+ * Flags to be passed to uv_fs_event_start().
+ */
+enum uv_fs_event_flags {
+  /*
+   * By default, if the fs event watcher is given a directory name, we will
+   * watch for all events in that directory. This flags overrides this behavior
+   * and makes fs_event report only changes to the directory entry itself. This
+   * flag does not affect individual files watched.
+   * This flag is currently not implemented yet on any backend.
+   */
+  UV_FS_EVENT_WATCH_ENTRY = 1,
+
+  /*
+   * By default uv_fs_event will try to use a kernel interface such as inotify
+   * or kqueue to detect events. This may not work on remote filesystems such
+   * as NFS mounts. This flag makes fs_event fall back to calling stat() on a
+   * regular interval.
+   * This flag is currently not implemented yet on any backend.
+   */
+  UV_FS_EVENT_STAT = 2,
+
+  /*
+   * By default, event watcher, when watching directory, is not registering
+   * (is ignoring) changes in it's subdirectories.
+   * This flag will override this behaviour on platforms that support it.
+   */
+  UV_FS_EVENT_RECURSIVE = 4
+};
+
+
+UV_EXTERN int uv_fs_event_init(uv_loop_t* loop, uv_fs_event_t* handle);
+UV_EXTERN int uv_fs_event_start(uv_fs_event_t* handle,
+                                uv_fs_event_cb cb,
+                                const char* path,
+                                unsigned int flags);
+UV_EXTERN int uv_fs_event_stop(uv_fs_event_t* handle);
+UV_EXTERN int uv_fs_event_getpath(uv_fs_event_t* handle,
+                                  char* buffer,
+                                  size_t* size);
+
+UV_EXTERN int uv_ip4_addr(const char* ip, int port, struct sockaddr_in* addr);
+UV_EXTERN int uv_ip6_addr(const char* ip, int port, struct sockaddr_in6* addr);
+
+UV_EXTERN int uv_ip4_name(const struct sockaddr_in* src, char* dst, size_t size);
+UV_EXTERN int uv_ip6_name(const struct sockaddr_in6* src, char* dst, size_t size);
+UV_EXTERN int uv_ip_name(const struct sockaddr* src, char* dst, size_t size);
+
+UV_EXTERN int uv_inet_ntop(int af, const void* src, char* dst, size_t size);
+UV_EXTERN int uv_inet_pton(int af, const char* src, void* dst);
+
+
+struct uv_random_s {
+  UV_REQ_FIELDS
+  /* read-only */
+  uv_loop_t* loop;
+  /* private */
+  int status;
+  void* buf;
+  size_t buflen;
+  uv_random_cb cb;
+  struct uv__work work_req;
+};
+
+UV_EXTERN int uv_random(uv_loop_t* loop,
+                        uv_random_t* req,
+                        void *buf,
+                        size_t buflen,
+                        unsigned flags,  /* For future extension; must be 0. */
+                        uv_random_cb cb);
+
+#if defined(IF_NAMESIZE)
+# define UV_IF_NAMESIZE (IF_NAMESIZE + 1)
+#elif defined(IFNAMSIZ)
+# define UV_IF_NAMESIZE (IFNAMSIZ + 1)
+#else
+# define UV_IF_NAMESIZE (16 + 1)
+#endif
+
+UV_EXTERN int uv_if_indextoname(unsigned int ifindex,
+                                char* buffer,
+                                size_t* size);
+UV_EXTERN int uv_if_indextoiid(unsigned int ifindex,
+                               char* buffer,
+                               size_t* size);
+
+UV_EXTERN int uv_exepath(char* buffer, size_t* size);
+
+UV_EXTERN int uv_cwd(char* buffer, size_t* size);
+
+UV_EXTERN int uv_chdir(const char* dir);
+
+UV_EXTERN uint64_t uv_get_free_memory(void);
+UV_EXTERN uint64_t uv_get_total_memory(void);
+UV_EXTERN uint64_t uv_get_constrained_memory(void);
+
+UV_EXTERN uint64_t uv_hrtime(void);
+UV_EXTERN void uv_sleep(unsigned int msec);
+
+UV_EXTERN void uv_disable_stdio_inheritance(void);
+
+UV_EXTERN int uv_dlopen(const char* filename, uv_lib_t* lib);
+UV_EXTERN void uv_dlclose(uv_lib_t* lib);
+UV_EXTERN int uv_dlsym(uv_lib_t* lib, const char* name, void** ptr);
+UV_EXTERN const char* uv_dlerror(const uv_lib_t* lib);
+
+UV_EXTERN int uv_mutex_init(uv_mutex_t* handle);
+UV_EXTERN int uv_mutex_init_recursive(uv_mutex_t* handle);
+UV_EXTERN void uv_mutex_destroy(uv_mutex_t* handle);
+UV_EXTERN void uv_mutex_lock(uv_mutex_t* handle);
+UV_EXTERN int uv_mutex_trylock(uv_mutex_t* handle);
+UV_EXTERN void uv_mutex_unlock(uv_mutex_t* handle);
+
+UV_EXTERN int uv_rwlock_init(uv_rwlock_t* rwlock);
+UV_EXTERN void uv_rwlock_destroy(uv_rwlock_t* rwlock);
+UV_EXTERN void uv_rwlock_rdlock(uv_rwlock_t* rwlock);
+UV_EXTERN int uv_rwlock_tryrdlock(uv_rwlock_t* rwlock);
+UV_EXTERN void uv_rwlock_rdunlock(uv_rwlock_t* rwlock);
+UV_EXTERN void uv_rwlock_wrlock(uv_rwlock_t* rwlock);
+UV_EXTERN int uv_rwlock_trywrlock(uv_rwlock_t* rwlock);
+UV_EXTERN void uv_rwlock_wrunlock(uv_rwlock_t* rwlock);
+
+UV_EXTERN int uv_sem_init(uv_sem_t* sem, unsigned int value);
+UV_EXTERN void uv_sem_destroy(uv_sem_t* sem);
+UV_EXTERN void uv_sem_post(uv_sem_t* sem);
+UV_EXTERN void uv_sem_wait(uv_sem_t* sem);
+UV_EXTERN int uv_sem_trywait(uv_sem_t* sem);
+
+UV_EXTERN int uv_cond_init(uv_cond_t* cond);
+UV_EXTERN void uv_cond_destroy(uv_cond_t* cond);
+UV_EXTERN void uv_cond_signal(uv_cond_t* cond);
+UV_EXTERN void uv_cond_broadcast(uv_cond_t* cond);
+
+UV_EXTERN int uv_barrier_init(uv_barrier_t* barrier, unsigned int count);
+UV_EXTERN void uv_barrier_destroy(uv_barrier_t* barrier);
+UV_EXTERN int uv_barrier_wait(uv_barrier_t* barrier);
+
+UV_EXTERN void uv_cond_wait(uv_cond_t* cond, uv_mutex_t* mutex);
+UV_EXTERN int uv_cond_timedwait(uv_cond_t* cond,
+                                uv_mutex_t* mutex,
+                                uint64_t timeout);
+
+UV_EXTERN void uv_once(uv_once_t* guard, void (*callback)(void));
+
+UV_EXTERN int uv_key_create(uv_key_t* key);
+UV_EXTERN void uv_key_delete(uv_key_t* key);
+UV_EXTERN void* uv_key_get(uv_key_t* key);
+UV_EXTERN void uv_key_set(uv_key_t* key, void* value);
+
+UV_EXTERN int uv_gettimeofday(uv_timeval64_t* tv);
+
+typedef void (*uv_thread_cb)(void* arg);
+
+UV_EXTERN int uv_thread_create(uv_thread_t* tid, uv_thread_cb entry, void* arg);
+
+typedef enum {
+  UV_THREAD_NO_FLAGS = 0x00,
+  UV_THREAD_HAS_STACK_SIZE = 0x01
+} uv_thread_create_flags;
+
+struct uv_thread_options_s {
+  unsigned int flags;
+  size_t stack_size;
+  /* More fields may be added at any time. */
+};
+
+typedef struct uv_thread_options_s uv_thread_options_t;
+
+UV_EXTERN int uv_thread_create_ex(uv_thread_t* tid,
+                                  const uv_thread_options_t* params,
+                                  uv_thread_cb entry,
+                                  void* arg);
+UV_EXTERN uv_thread_t uv_thread_self(void);
+UV_EXTERN int uv_thread_join(uv_thread_t *tid);
+UV_EXTERN int uv_thread_equal(const uv_thread_t* t1, const uv_thread_t* t2);
+
+/* The presence of these unions force similar struct layout. */
+#define XX(_, name) uv_ ## name ## _t name;
+union uv_any_handle {
+  UV_HANDLE_TYPE_MAP(XX)
+};
+
+union uv_any_req {
+  UV_REQ_TYPE_MAP(XX)
+};
+#undef XX
+
+
+struct uv_loop_s {
+  /* User data - use this for whatever. */
+  void* data;
+  /* Loop reference counting. */
+  unsigned int active_handles;
+  void* handle_queue[2];
+  union {
+    void* unused;
+    unsigned int count;
+  } active_reqs;
+  /* Internal storage for future extensions. */
+  void* internal_fields;
+  /* Internal flag to signal loop stop. */
+  unsigned int stop_flag;
+  UV_LOOP_PRIVATE_FIELDS
+};
+
+UV_EXTERN void* uv_loop_get_data(const uv_loop_t*);
+UV_EXTERN void uv_loop_set_data(uv_loop_t*, void* data);
+
+/* Don't export the private CPP symbols. */
+#undef UV_HANDLE_TYPE_PRIVATE
+#undef UV_REQ_TYPE_PRIVATE
+#undef UV_REQ_PRIVATE_FIELDS
+#undef UV_STREAM_PRIVATE_FIELDS
+#undef UV_TCP_PRIVATE_FIELDS
+#undef UV_PREPARE_PRIVATE_FIELDS
+#undef UV_CHECK_PRIVATE_FIELDS
+#undef UV_IDLE_PRIVATE_FIELDS
+#undef UV_ASYNC_PRIVATE_FIELDS
+#undef UV_TIMER_PRIVATE_FIELDS
+#undef UV_GETADDRINFO_PRIVATE_FIELDS
+#undef UV_GETNAMEINFO_PRIVATE_FIELDS
+#undef UV_FS_REQ_PRIVATE_FIELDS
+#undef UV_WORK_PRIVATE_FIELDS
+#undef UV_FS_EVENT_PRIVATE_FIELDS
+#undef UV_SIGNAL_PRIVATE_FIELDS
+#undef UV_LOOP_PRIVATE_FIELDS
+#undef UV_LOOP_PRIVATE_PLATFORM_FIELDS
+#undef UV__ERR
+
+#endif /* UV_H */
diff --git a/wpinet/src/main/native/thirdparty/libuv/include/uv/bsd.h b/wpinet/src/main/native/thirdparty/libuv/include/uv/bsd.h
new file mode 100644
index 0000000..2d72b3d
--- /dev/null
+++ b/wpinet/src/main/native/thirdparty/libuv/include/uv/bsd.h
@@ -0,0 +1,34 @@
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#ifndef UV_BSD_H
+#define UV_BSD_H
+
+#define UV_PLATFORM_FS_EVENT_FIELDS                                           \
+  uv__io_t event_watcher;                                                     \
+
+#define UV_IO_PRIVATE_PLATFORM_FIELDS                                         \
+  int rcount;                                                                 \
+  int wcount;                                                                 \
+
+#define UV_HAVE_KQUEUE 1
+
+#endif /* UV_BSD_H */
diff --git a/wpinet/src/main/native/thirdparty/libuv/include/uv/darwin.h b/wpinet/src/main/native/thirdparty/libuv/include/uv/darwin.h
new file mode 100644
index 0000000..d226415
--- /dev/null
+++ b/wpinet/src/main/native/thirdparty/libuv/include/uv/darwin.h
@@ -0,0 +1,61 @@
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#ifndef UV_DARWIN_H
+#define UV_DARWIN_H
+
+#if defined(__APPLE__) && defined(__MACH__)
+# include <mach/mach.h>
+# include <mach/task.h>
+# include <mach/semaphore.h>
+# include <TargetConditionals.h>
+# define UV_PLATFORM_SEM_T semaphore_t
+#endif
+
+#define UV_IO_PRIVATE_PLATFORM_FIELDS                                         \
+  int rcount;                                                                 \
+  int wcount;                                                                 \
+
+#define UV_PLATFORM_LOOP_FIELDS                                               \
+  uv_thread_t cf_thread;                                                      \
+  void* _cf_reserved;                                                         \
+  void* cf_state;                                                             \
+  uv_mutex_t cf_mutex;                                                        \
+  uv_sem_t cf_sem;                                                            \
+  void* cf_signals[2];                                                        \
+
+#define UV_PLATFORM_FS_EVENT_FIELDS                                           \
+  uv__io_t event_watcher;                                                     \
+  char* realpath;                                                             \
+  int realpath_len;                                                           \
+  int cf_flags;                                                               \
+  uv_async_t* cf_cb;                                                          \
+  void* cf_events[2];                                                         \
+  void* cf_member[2];                                                         \
+  int cf_error;                                                               \
+  uv_mutex_t cf_mutex;                                                        \
+
+#define UV_STREAM_PRIVATE_PLATFORM_FIELDS                                     \
+  void* select;                                                               \
+
+#define UV_HAVE_KQUEUE 1
+
+#endif /* UV_DARWIN_H */
diff --git a/wpinet/src/main/native/thirdparty/libuv/include/uv/errno.h b/wpinet/src/main/native/thirdparty/libuv/include/uv/errno.h
new file mode 100644
index 0000000..71906b3
--- /dev/null
+++ b/wpinet/src/main/native/thirdparty/libuv/include/uv/errno.h
@@ -0,0 +1,460 @@
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#ifndef UV_ERRNO_H_
+#define UV_ERRNO_H_
+
+#include <errno.h>
+#if EDOM > 0
+# define UV__ERR(x) (-(x))
+#else
+# define UV__ERR(x) (x)
+#endif
+
+#define UV__EOF     (-4095)
+#define UV__UNKNOWN (-4094)
+
+#define UV__EAI_ADDRFAMILY  (-3000)
+#define UV__EAI_AGAIN       (-3001)
+#define UV__EAI_BADFLAGS    (-3002)
+#define UV__EAI_CANCELED    (-3003)
+#define UV__EAI_FAIL        (-3004)
+#define UV__EAI_FAMILY      (-3005)
+#define UV__EAI_MEMORY      (-3006)
+#define UV__EAI_NODATA      (-3007)
+#define UV__EAI_NONAME      (-3008)
+#define UV__EAI_OVERFLOW    (-3009)
+#define UV__EAI_SERVICE     (-3010)
+#define UV__EAI_SOCKTYPE    (-3011)
+#define UV__EAI_BADHINTS    (-3013)
+#define UV__EAI_PROTOCOL    (-3014)
+
+/* Only map to the system errno on non-Windows platforms. It's apparently
+ * a fairly common practice for Windows programmers to redefine errno codes.
+ */
+#if defined(E2BIG) && !defined(_WIN32)
+# define UV__E2BIG UV__ERR(E2BIG)
+#else
+# define UV__E2BIG (-4093)
+#endif
+
+#if defined(EACCES) && !defined(_WIN32)
+# define UV__EACCES UV__ERR(EACCES)
+#else
+# define UV__EACCES (-4092)
+#endif
+
+#if defined(EADDRINUSE) && !defined(_WIN32)
+# define UV__EADDRINUSE UV__ERR(EADDRINUSE)
+#else
+# define UV__EADDRINUSE (-4091)
+#endif
+
+#if defined(EADDRNOTAVAIL) && !defined(_WIN32)
+# define UV__EADDRNOTAVAIL UV__ERR(EADDRNOTAVAIL)
+#else
+# define UV__EADDRNOTAVAIL (-4090)
+#endif
+
+#if defined(EAFNOSUPPORT) && !defined(_WIN32)
+# define UV__EAFNOSUPPORT UV__ERR(EAFNOSUPPORT)
+#else
+# define UV__EAFNOSUPPORT (-4089)
+#endif
+
+#if defined(EAGAIN) && !defined(_WIN32)
+# define UV__EAGAIN UV__ERR(EAGAIN)
+#else
+# define UV__EAGAIN (-4088)
+#endif
+
+#if defined(EALREADY) && !defined(_WIN32)
+# define UV__EALREADY UV__ERR(EALREADY)
+#else
+# define UV__EALREADY (-4084)
+#endif
+
+#if defined(EBADF) && !defined(_WIN32)
+# define UV__EBADF UV__ERR(EBADF)
+#else
+# define UV__EBADF (-4083)
+#endif
+
+#if defined(EBUSY) && !defined(_WIN32)
+# define UV__EBUSY UV__ERR(EBUSY)
+#else
+# define UV__EBUSY (-4082)
+#endif
+
+#if defined(ECANCELED) && !defined(_WIN32)
+# define UV__ECANCELED UV__ERR(ECANCELED)
+#else
+# define UV__ECANCELED (-4081)
+#endif
+
+#if defined(ECHARSET) && !defined(_WIN32)
+# define UV__ECHARSET UV__ERR(ECHARSET)
+#else
+# define UV__ECHARSET (-4080)
+#endif
+
+#if defined(ECONNABORTED) && !defined(_WIN32)
+# define UV__ECONNABORTED UV__ERR(ECONNABORTED)
+#else
+# define UV__ECONNABORTED (-4079)
+#endif
+
+#if defined(ECONNREFUSED) && !defined(_WIN32)
+# define UV__ECONNREFUSED UV__ERR(ECONNREFUSED)
+#else
+# define UV__ECONNREFUSED (-4078)
+#endif
+
+#if defined(ECONNRESET) && !defined(_WIN32)
+# define UV__ECONNRESET UV__ERR(ECONNRESET)
+#else
+# define UV__ECONNRESET (-4077)
+#endif
+
+#if defined(EDESTADDRREQ) && !defined(_WIN32)
+# define UV__EDESTADDRREQ UV__ERR(EDESTADDRREQ)
+#else
+# define UV__EDESTADDRREQ (-4076)
+#endif
+
+#if defined(EEXIST) && !defined(_WIN32)
+# define UV__EEXIST UV__ERR(EEXIST)
+#else
+# define UV__EEXIST (-4075)
+#endif
+
+#if defined(EFAULT) && !defined(_WIN32)
+# define UV__EFAULT UV__ERR(EFAULT)
+#else
+# define UV__EFAULT (-4074)
+#endif
+
+#if defined(EHOSTUNREACH) && !defined(_WIN32)
+# define UV__EHOSTUNREACH UV__ERR(EHOSTUNREACH)
+#else
+# define UV__EHOSTUNREACH (-4073)
+#endif
+
+#if defined(EINTR) && !defined(_WIN32)
+# define UV__EINTR UV__ERR(EINTR)
+#else
+# define UV__EINTR (-4072)
+#endif
+
+#if defined(EINVAL) && !defined(_WIN32)
+# define UV__EINVAL UV__ERR(EINVAL)
+#else
+# define UV__EINVAL (-4071)
+#endif
+
+#if defined(EIO) && !defined(_WIN32)
+# define UV__EIO UV__ERR(EIO)
+#else
+# define UV__EIO (-4070)
+#endif
+
+#if defined(EISCONN) && !defined(_WIN32)
+# define UV__EISCONN UV__ERR(EISCONN)
+#else
+# define UV__EISCONN (-4069)
+#endif
+
+#if defined(EISDIR) && !defined(_WIN32)
+# define UV__EISDIR UV__ERR(EISDIR)
+#else
+# define UV__EISDIR (-4068)
+#endif
+
+#if defined(ELOOP) && !defined(_WIN32)
+# define UV__ELOOP UV__ERR(ELOOP)
+#else
+# define UV__ELOOP (-4067)
+#endif
+
+#if defined(EMFILE) && !defined(_WIN32)
+# define UV__EMFILE UV__ERR(EMFILE)
+#else
+# define UV__EMFILE (-4066)
+#endif
+
+#if defined(EMSGSIZE) && !defined(_WIN32)
+# define UV__EMSGSIZE UV__ERR(EMSGSIZE)
+#else
+# define UV__EMSGSIZE (-4065)
+#endif
+
+#if defined(ENAMETOOLONG) && !defined(_WIN32)
+# define UV__ENAMETOOLONG UV__ERR(ENAMETOOLONG)
+#else
+# define UV__ENAMETOOLONG (-4064)
+#endif
+
+#if defined(ENETDOWN) && !defined(_WIN32)
+# define UV__ENETDOWN UV__ERR(ENETDOWN)
+#else
+# define UV__ENETDOWN (-4063)
+#endif
+
+#if defined(ENETUNREACH) && !defined(_WIN32)
+# define UV__ENETUNREACH UV__ERR(ENETUNREACH)
+#else
+# define UV__ENETUNREACH (-4062)
+#endif
+
+#if defined(ENFILE) && !defined(_WIN32)
+# define UV__ENFILE UV__ERR(ENFILE)
+#else
+# define UV__ENFILE (-4061)
+#endif
+
+#if defined(ENOBUFS) && !defined(_WIN32)
+# define UV__ENOBUFS UV__ERR(ENOBUFS)
+#else
+# define UV__ENOBUFS (-4060)
+#endif
+
+#if defined(ENODEV) && !defined(_WIN32)
+# define UV__ENODEV UV__ERR(ENODEV)
+#else
+# define UV__ENODEV (-4059)
+#endif
+
+#if defined(ENOENT) && !defined(_WIN32)
+# define UV__ENOENT UV__ERR(ENOENT)
+#else
+# define UV__ENOENT (-4058)
+#endif
+
+#if defined(ENOMEM) && !defined(_WIN32)
+# define UV__ENOMEM UV__ERR(ENOMEM)
+#else
+# define UV__ENOMEM (-4057)
+#endif
+
+#if defined(ENONET) && !defined(_WIN32)
+# define UV__ENONET UV__ERR(ENONET)
+#else
+# define UV__ENONET (-4056)
+#endif
+
+#if defined(ENOSPC) && !defined(_WIN32)
+# define UV__ENOSPC UV__ERR(ENOSPC)
+#else
+# define UV__ENOSPC (-4055)
+#endif
+
+#if defined(ENOSYS) && !defined(_WIN32)
+# define UV__ENOSYS UV__ERR(ENOSYS)
+#else
+# define UV__ENOSYS (-4054)
+#endif
+
+#if defined(ENOTCONN) && !defined(_WIN32)
+# define UV__ENOTCONN UV__ERR(ENOTCONN)
+#else
+# define UV__ENOTCONN (-4053)
+#endif
+
+#if defined(ENOTDIR) && !defined(_WIN32)
+# define UV__ENOTDIR UV__ERR(ENOTDIR)
+#else
+# define UV__ENOTDIR (-4052)
+#endif
+
+#if defined(ENOTEMPTY) && !defined(_WIN32)
+# define UV__ENOTEMPTY UV__ERR(ENOTEMPTY)
+#else
+# define UV__ENOTEMPTY (-4051)
+#endif
+
+#if defined(ENOTSOCK) && !defined(_WIN32)
+# define UV__ENOTSOCK UV__ERR(ENOTSOCK)
+#else
+# define UV__ENOTSOCK (-4050)
+#endif
+
+#if defined(ENOTSUP) && !defined(_WIN32)
+# define UV__ENOTSUP UV__ERR(ENOTSUP)
+#else
+# define UV__ENOTSUP (-4049)
+#endif
+
+#if defined(EPERM) && !defined(_WIN32)
+# define UV__EPERM UV__ERR(EPERM)
+#else
+# define UV__EPERM (-4048)
+#endif
+
+#if defined(EPIPE) && !defined(_WIN32)
+# define UV__EPIPE UV__ERR(EPIPE)
+#else
+# define UV__EPIPE (-4047)
+#endif
+
+#if defined(EPROTO) && !defined(_WIN32)
+# define UV__EPROTO UV__ERR(EPROTO)
+#else
+# define UV__EPROTO (-4046)
+#endif
+
+#if defined(EPROTONOSUPPORT) && !defined(_WIN32)
+# define UV__EPROTONOSUPPORT UV__ERR(EPROTONOSUPPORT)
+#else
+# define UV__EPROTONOSUPPORT (-4045)
+#endif
+
+#if defined(EPROTOTYPE) && !defined(_WIN32)
+# define UV__EPROTOTYPE UV__ERR(EPROTOTYPE)
+#else
+# define UV__EPROTOTYPE (-4044)
+#endif
+
+#if defined(EROFS) && !defined(_WIN32)
+# define UV__EROFS UV__ERR(EROFS)
+#else
+# define UV__EROFS (-4043)
+#endif
+
+#if defined(ESHUTDOWN) && !defined(_WIN32)
+# define UV__ESHUTDOWN UV__ERR(ESHUTDOWN)
+#else
+# define UV__ESHUTDOWN (-4042)
+#endif
+
+#if defined(ESPIPE) && !defined(_WIN32)
+# define UV__ESPIPE UV__ERR(ESPIPE)
+#else
+# define UV__ESPIPE (-4041)
+#endif
+
+#if defined(ESRCH) && !defined(_WIN32)
+# define UV__ESRCH UV__ERR(ESRCH)
+#else
+# define UV__ESRCH (-4040)
+#endif
+
+#if defined(ETIMEDOUT) && !defined(_WIN32)
+# define UV__ETIMEDOUT UV__ERR(ETIMEDOUT)
+#else
+# define UV__ETIMEDOUT (-4039)
+#endif
+
+#if defined(ETXTBSY) && !defined(_WIN32)
+# define UV__ETXTBSY UV__ERR(ETXTBSY)
+#else
+# define UV__ETXTBSY (-4038)
+#endif
+
+#if defined(EXDEV) && !defined(_WIN32)
+# define UV__EXDEV UV__ERR(EXDEV)
+#else
+# define UV__EXDEV (-4037)
+#endif
+
+#if defined(EFBIG) && !defined(_WIN32)
+# define UV__EFBIG UV__ERR(EFBIG)
+#else
+# define UV__EFBIG (-4036)
+#endif
+
+#if defined(ENOPROTOOPT) && !defined(_WIN32)
+# define UV__ENOPROTOOPT UV__ERR(ENOPROTOOPT)
+#else
+# define UV__ENOPROTOOPT (-4035)
+#endif
+
+#if defined(ERANGE) && !defined(_WIN32)
+# define UV__ERANGE UV__ERR(ERANGE)
+#else
+# define UV__ERANGE (-4034)
+#endif
+
+#if defined(ENXIO) && !defined(_WIN32)
+# define UV__ENXIO UV__ERR(ENXIO)
+#else
+# define UV__ENXIO (-4033)
+#endif
+
+#if defined(EMLINK) && !defined(_WIN32)
+# define UV__EMLINK UV__ERR(EMLINK)
+#else
+# define UV__EMLINK (-4032)
+#endif
+
+/* EHOSTDOWN is not visible on BSD-like systems when _POSIX_C_SOURCE is
+ * defined. Fortunately, its value is always 64 so it's possible albeit
+ * icky to hard-code it.
+ */
+#if defined(EHOSTDOWN) && !defined(_WIN32)
+# define UV__EHOSTDOWN UV__ERR(EHOSTDOWN)
+#elif defined(__APPLE__) || \
+      defined(__DragonFly__) || \
+      defined(__FreeBSD__) || \
+      defined(__FreeBSD_kernel__) || \
+      defined(__NetBSD__) || \
+      defined(__OpenBSD__)
+# define UV__EHOSTDOWN (-64)
+#else
+# define UV__EHOSTDOWN (-4031)
+#endif
+
+#if defined(EREMOTEIO) && !defined(_WIN32)
+# define UV__EREMOTEIO UV__ERR(EREMOTEIO)
+#else
+# define UV__EREMOTEIO (-4030)
+#endif
+
+#if defined(ENOTTY) && !defined(_WIN32)
+# define UV__ENOTTY UV__ERR(ENOTTY)
+#else
+# define UV__ENOTTY (-4029)
+#endif
+
+#if defined(EFTYPE) && !defined(_WIN32)
+# define UV__EFTYPE UV__ERR(EFTYPE)
+#else
+# define UV__EFTYPE (-4028)
+#endif
+
+#if defined(EILSEQ) && !defined(_WIN32)
+# define UV__EILSEQ UV__ERR(EILSEQ)
+#else
+# define UV__EILSEQ (-4027)
+#endif
+
+#if defined(EOVERFLOW) && !defined(_WIN32)
+# define UV__EOVERFLOW UV__ERR(EOVERFLOW)
+#else
+# define UV__EOVERFLOW (-4026)
+#endif
+
+#if defined(ESOCKTNOSUPPORT) && !defined(_WIN32)
+# define UV__ESOCKTNOSUPPORT UV__ERR(ESOCKTNOSUPPORT)
+#else
+# define UV__ESOCKTNOSUPPORT (-4025)
+#endif
+
+#endif /* UV_ERRNO_H_ */
diff --git a/wpinet/src/main/native/thirdparty/libuv/include/uv/linux.h b/wpinet/src/main/native/thirdparty/libuv/include/uv/linux.h
new file mode 100644
index 0000000..9b38405
--- /dev/null
+++ b/wpinet/src/main/native/thirdparty/libuv/include/uv/linux.h
@@ -0,0 +1,34 @@
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#ifndef UV_LINUX_H
+#define UV_LINUX_H
+
+#define UV_PLATFORM_LOOP_FIELDS                                               \
+  uv__io_t inotify_read_watcher;                                              \
+  void* inotify_watchers;                                                     \
+  int inotify_fd;                                                             \
+
+#define UV_PLATFORM_FS_EVENT_FIELDS                                           \
+  void* watchers[2];                                                          \
+  int wd;                                                                     \
+
+#endif /* UV_LINUX_H */
diff --git a/wpinet/src/main/native/thirdparty/libuv/include/uv/posix.h b/wpinet/src/main/native/thirdparty/libuv/include/uv/posix.h
new file mode 100644
index 0000000..9a96634
--- /dev/null
+++ b/wpinet/src/main/native/thirdparty/libuv/include/uv/posix.h
@@ -0,0 +1,31 @@
+/* Copyright libuv project contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#ifndef UV_POSIX_H
+#define UV_POSIX_H
+
+#define UV_PLATFORM_LOOP_FIELDS                                               \
+  struct pollfd* poll_fds;                                                    \
+  size_t poll_fds_used;                                                       \
+  size_t poll_fds_size;                                                       \
+  unsigned char poll_fds_iterating;                                           \
+
+#endif /* UV_POSIX_H */
diff --git a/wpinet/src/main/native/thirdparty/libuv/include/uv/threadpool.h b/wpinet/src/main/native/thirdparty/libuv/include/uv/threadpool.h
new file mode 100644
index 0000000..9708ebd
--- /dev/null
+++ b/wpinet/src/main/native/thirdparty/libuv/include/uv/threadpool.h
@@ -0,0 +1,37 @@
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+/*
+ * This file is private to libuv. It provides common functionality to both
+ * Windows and Unix backends.
+ */
+
+#ifndef UV_THREADPOOL_H_
+#define UV_THREADPOOL_H_
+
+struct uv__work {
+  void (*work)(struct uv__work *w);
+  void (*done)(struct uv__work *w, int status);
+  struct uv_loop_s* loop;
+  void* wq[2];
+};
+
+#endif /* UV_THREADPOOL_H_ */
diff --git a/wpinet/src/main/native/thirdparty/libuv/include/uv/tree.h b/wpinet/src/main/native/thirdparty/libuv/include/uv/tree.h
new file mode 100644
index 0000000..2b28835
--- /dev/null
+++ b/wpinet/src/main/native/thirdparty/libuv/include/uv/tree.h
@@ -0,0 +1,768 @@
+/*-
+ * Copyright 2002 Niels Provos <provos@citi.umich.edu>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef  UV_TREE_H_
+#define  UV_TREE_H_
+
+#ifndef UV__UNUSED
+# if __GNUC__
+#  define UV__UNUSED __attribute__((unused))
+# else
+#  define UV__UNUSED
+# endif
+#endif
+
+/*
+ * This file defines data structures for different types of trees:
+ * splay trees and red-black trees.
+ *
+ * A splay tree is a self-organizing data structure.  Every operation
+ * on the tree causes a splay to happen.  The splay moves the requested
+ * node to the root of the tree and partly rebalances it.
+ *
+ * This has the benefit that request locality causes faster lookups as
+ * the requested nodes move to the top of the tree.  On the other hand,
+ * every lookup causes memory writes.
+ *
+ * The Balance Theorem bounds the total access time for m operations
+ * and n inserts on an initially empty tree as O((m + n)lg n).  The
+ * amortized cost for a sequence of m accesses to a splay tree is O(lg n);
+ *
+ * A red-black tree is a binary search tree with the node color as an
+ * extra attribute.  It fulfills a set of conditions:
+ *  - every search path from the root to a leaf consists of the
+ *    same number of black nodes,
+ *  - each red node (except for the root) has a black parent,
+ *  - each leaf node is black.
+ *
+ * Every operation on a red-black tree is bounded as O(lg n).
+ * The maximum height of a red-black tree is 2lg (n+1).
+ */
+
+#define SPLAY_HEAD(name, type)                                                \
+struct name {                                                                 \
+  struct type *sph_root; /* root of the tree */                               \
+}
+
+#define SPLAY_INITIALIZER(root)                                               \
+  { NULL }
+
+#define SPLAY_INIT(root) do {                                                 \
+  (root)->sph_root = NULL;                                                    \
+} while (/*CONSTCOND*/ 0)
+
+#define SPLAY_ENTRY(type)                                                     \
+struct {                                                                      \
+  struct type *spe_left;          /* left element */                          \
+  struct type *spe_right;         /* right element */                         \
+}
+
+#define SPLAY_LEFT(elm, field)    (elm)->field.spe_left
+#define SPLAY_RIGHT(elm, field)   (elm)->field.spe_right
+#define SPLAY_ROOT(head)          (head)->sph_root
+#define SPLAY_EMPTY(head)         (SPLAY_ROOT(head) == NULL)
+
+/* SPLAY_ROTATE_{LEFT,RIGHT} expect that tmp hold SPLAY_{RIGHT,LEFT} */
+#define SPLAY_ROTATE_RIGHT(head, tmp, field) do {                             \
+  SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(tmp, field);              \
+  SPLAY_RIGHT(tmp, field) = (head)->sph_root;                                 \
+  (head)->sph_root = tmp;                                                     \
+} while (/*CONSTCOND*/ 0)
+
+#define SPLAY_ROTATE_LEFT(head, tmp, field) do {                              \
+  SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(tmp, field);              \
+  SPLAY_LEFT(tmp, field) = (head)->sph_root;                                  \
+  (head)->sph_root = tmp;                                                     \
+} while (/*CONSTCOND*/ 0)
+
+#define SPLAY_LINKLEFT(head, tmp, field) do {                                 \
+  SPLAY_LEFT(tmp, field) = (head)->sph_root;                                  \
+  tmp = (head)->sph_root;                                                     \
+  (head)->sph_root = SPLAY_LEFT((head)->sph_root, field);                     \
+} while (/*CONSTCOND*/ 0)
+
+#define SPLAY_LINKRIGHT(head, tmp, field) do {                                \
+  SPLAY_RIGHT(tmp, field) = (head)->sph_root;                                 \
+  tmp = (head)->sph_root;                                                     \
+  (head)->sph_root = SPLAY_RIGHT((head)->sph_root, field);                    \
+} while (/*CONSTCOND*/ 0)
+
+#define SPLAY_ASSEMBLE(head, node, left, right, field) do {                   \
+  SPLAY_RIGHT(left, field) = SPLAY_LEFT((head)->sph_root, field);             \
+  SPLAY_LEFT(right, field) = SPLAY_RIGHT((head)->sph_root, field);            \
+  SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(node, field);             \
+  SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(node, field);             \
+} while (/*CONSTCOND*/ 0)
+
+/* Generates prototypes and inline functions */
+
+#define SPLAY_PROTOTYPE(name, type, field, cmp)                               \
+void name##_SPLAY(struct name *, struct type *);                              \
+void name##_SPLAY_MINMAX(struct name *, int);                                 \
+struct type *name##_SPLAY_INSERT(struct name *, struct type *);               \
+struct type *name##_SPLAY_REMOVE(struct name *, struct type *);               \
+                                                                              \
+/* Finds the node with the same key as elm */                                 \
+static __inline struct type *                                                 \
+name##_SPLAY_FIND(struct name *head, struct type *elm)                        \
+{                                                                             \
+  if (SPLAY_EMPTY(head))                                                      \
+    return(NULL);                                                             \
+  name##_SPLAY(head, elm);                                                    \
+  if ((cmp)(elm, (head)->sph_root) == 0)                                      \
+    return (head->sph_root);                                                  \
+  return (NULL);                                                              \
+}                                                                             \
+                                                                              \
+static __inline struct type *                                                 \
+name##_SPLAY_NEXT(struct name *head, struct type *elm)                        \
+{                                                                             \
+  name##_SPLAY(head, elm);                                                    \
+  if (SPLAY_RIGHT(elm, field) != NULL) {                                      \
+    elm = SPLAY_RIGHT(elm, field);                                            \
+    while (SPLAY_LEFT(elm, field) != NULL) {                                  \
+      elm = SPLAY_LEFT(elm, field);                                           \
+    }                                                                         \
+  } else                                                                      \
+    elm = NULL;                                                               \
+  return (elm);                                                               \
+}                                                                             \
+                                                                              \
+static __inline struct type *                                                 \
+name##_SPLAY_MIN_MAX(struct name *head, int val)                              \
+{                                                                             \
+  name##_SPLAY_MINMAX(head, val);                                             \
+  return (SPLAY_ROOT(head));                                                  \
+}
+
+/* Main splay operation.
+ * Moves node close to the key of elm to top
+ */
+#define SPLAY_GENERATE(name, type, field, cmp)                                \
+struct type *                                                                 \
+name##_SPLAY_INSERT(struct name *head, struct type *elm)                      \
+{                                                                             \
+    if (SPLAY_EMPTY(head)) {                                                  \
+      SPLAY_LEFT(elm, field) = SPLAY_RIGHT(elm, field) = NULL;                \
+    } else {                                                                  \
+      int __comp;                                                             \
+      name##_SPLAY(head, elm);                                                \
+      __comp = (cmp)(elm, (head)->sph_root);                                  \
+      if(__comp < 0) {                                                        \
+        SPLAY_LEFT(elm, field) = SPLAY_LEFT((head)->sph_root, field);         \
+        SPLAY_RIGHT(elm, field) = (head)->sph_root;                           \
+        SPLAY_LEFT((head)->sph_root, field) = NULL;                           \
+      } else if (__comp > 0) {                                                \
+        SPLAY_RIGHT(elm, field) = SPLAY_RIGHT((head)->sph_root, field);       \
+        SPLAY_LEFT(elm, field) = (head)->sph_root;                            \
+        SPLAY_RIGHT((head)->sph_root, field) = NULL;                          \
+      } else                                                                  \
+        return ((head)->sph_root);                                            \
+    }                                                                         \
+    (head)->sph_root = (elm);                                                 \
+    return (NULL);                                                            \
+}                                                                             \
+                                                                              \
+struct type *                                                                 \
+name##_SPLAY_REMOVE(struct name *head, struct type *elm)                      \
+{                                                                             \
+  struct type *__tmp;                                                         \
+  if (SPLAY_EMPTY(head))                                                      \
+    return (NULL);                                                            \
+  name##_SPLAY(head, elm);                                                    \
+  if ((cmp)(elm, (head)->sph_root) == 0) {                                    \
+    if (SPLAY_LEFT((head)->sph_root, field) == NULL) {                        \
+      (head)->sph_root = SPLAY_RIGHT((head)->sph_root, field);                \
+    } else {                                                                  \
+      __tmp = SPLAY_RIGHT((head)->sph_root, field);                           \
+      (head)->sph_root = SPLAY_LEFT((head)->sph_root, field);                 \
+      name##_SPLAY(head, elm);                                                \
+      SPLAY_RIGHT((head)->sph_root, field) = __tmp;                           \
+    }                                                                         \
+    return (elm);                                                             \
+  }                                                                           \
+  return (NULL);                                                              \
+}                                                                             \
+                                                                              \
+void                                                                          \
+name##_SPLAY(struct name *head, struct type *elm)                             \
+{                                                                             \
+  struct type __node, *__left, *__right, *__tmp;                              \
+  int __comp;                                                                 \
+                                                                              \
+  SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;            \
+  __left = __right = &__node;                                                 \
+                                                                              \
+  while ((__comp = (cmp)(elm, (head)->sph_root)) != 0) {                      \
+    if (__comp < 0) {                                                         \
+      __tmp = SPLAY_LEFT((head)->sph_root, field);                            \
+      if (__tmp == NULL)                                                      \
+        break;                                                                \
+      if ((cmp)(elm, __tmp) < 0){                                             \
+        SPLAY_ROTATE_RIGHT(head, __tmp, field);                               \
+        if (SPLAY_LEFT((head)->sph_root, field) == NULL)                      \
+          break;                                                              \
+      }                                                                       \
+      SPLAY_LINKLEFT(head, __right, field);                                   \
+    } else if (__comp > 0) {                                                  \
+      __tmp = SPLAY_RIGHT((head)->sph_root, field);                           \
+      if (__tmp == NULL)                                                      \
+        break;                                                                \
+      if ((cmp)(elm, __tmp) > 0){                                             \
+        SPLAY_ROTATE_LEFT(head, __tmp, field);                                \
+        if (SPLAY_RIGHT((head)->sph_root, field) == NULL)                     \
+          break;                                                              \
+      }                                                                       \
+      SPLAY_LINKRIGHT(head, __left, field);                                   \
+    }                                                                         \
+  }                                                                           \
+  SPLAY_ASSEMBLE(head, &__node, __left, __right, field);                      \
+}                                                                             \
+                                                                              \
+/* Splay with either the minimum or the maximum element                       \
+ * Used to find minimum or maximum element in tree.                           \
+ */                                                                           \
+void name##_SPLAY_MINMAX(struct name *head, int __comp)                       \
+{                                                                             \
+  struct type __node, *__left, *__right, *__tmp;                              \
+                                                                              \
+  SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;            \
+  __left = __right = &__node;                                                 \
+                                                                              \
+  for (;;) {                                                                  \
+    if (__comp < 0) {                                                         \
+      __tmp = SPLAY_LEFT((head)->sph_root, field);                            \
+      if (__tmp == NULL)                                                      \
+        break;                                                                \
+      if (__comp < 0){                                                        \
+        SPLAY_ROTATE_RIGHT(head, __tmp, field);                               \
+        if (SPLAY_LEFT((head)->sph_root, field) == NULL)                      \
+          break;                                                              \
+      }                                                                       \
+      SPLAY_LINKLEFT(head, __right, field);                                   \
+    } else if (__comp > 0) {                                                  \
+      __tmp = SPLAY_RIGHT((head)->sph_root, field);                           \
+      if (__tmp == NULL)                                                      \
+        break;                                                                \
+      if (__comp > 0) {                                                       \
+        SPLAY_ROTATE_LEFT(head, __tmp, field);                                \
+        if (SPLAY_RIGHT((head)->sph_root, field) == NULL)                     \
+          break;                                                              \
+      }                                                                       \
+      SPLAY_LINKRIGHT(head, __left, field);                                   \
+    }                                                                         \
+  }                                                                           \
+  SPLAY_ASSEMBLE(head, &__node, __left, __right, field);                      \
+}
+
+#define SPLAY_NEGINF  -1
+#define SPLAY_INF     1
+
+#define SPLAY_INSERT(name, x, y)  name##_SPLAY_INSERT(x, y)
+#define SPLAY_REMOVE(name, x, y)  name##_SPLAY_REMOVE(x, y)
+#define SPLAY_FIND(name, x, y)    name##_SPLAY_FIND(x, y)
+#define SPLAY_NEXT(name, x, y)    name##_SPLAY_NEXT(x, y)
+#define SPLAY_MIN(name, x)        (SPLAY_EMPTY(x) ? NULL                      \
+                                  : name##_SPLAY_MIN_MAX(x, SPLAY_NEGINF))
+#define SPLAY_MAX(name, x)        (SPLAY_EMPTY(x) ? NULL                      \
+                                  : name##_SPLAY_MIN_MAX(x, SPLAY_INF))
+
+#define SPLAY_FOREACH(x, name, head)                                          \
+  for ((x) = SPLAY_MIN(name, head);                                           \
+       (x) != NULL;                                                           \
+       (x) = SPLAY_NEXT(name, head, x))
+
+/* Macros that define a red-black tree */
+#define RB_HEAD(name, type)                                                   \
+struct name {                                                                 \
+  struct type *rbh_root; /* root of the tree */                               \
+}
+
+#define RB_INITIALIZER(root)                                                  \
+  { NULL }
+
+#define RB_INIT(root) do {                                                    \
+  (root)->rbh_root = NULL;                                                    \
+} while (/*CONSTCOND*/ 0)
+
+#define RB_BLACK  0
+#define RB_RED    1
+#define RB_ENTRY(type)                                                        \
+struct {                                                                      \
+  struct type *rbe_left;        /* left element */                            \
+  struct type *rbe_right;       /* right element */                           \
+  struct type *rbe_parent;      /* parent element */                          \
+  int rbe_color;                /* node color */                              \
+}
+
+#define RB_LEFT(elm, field)     (elm)->field.rbe_left
+#define RB_RIGHT(elm, field)    (elm)->field.rbe_right
+#define RB_PARENT(elm, field)   (elm)->field.rbe_parent
+#define RB_COLOR(elm, field)    (elm)->field.rbe_color
+#define RB_ROOT(head)           (head)->rbh_root
+#define RB_EMPTY(head)          (RB_ROOT(head) == NULL)
+
+#define RB_SET(elm, parent, field) do {                                       \
+  RB_PARENT(elm, field) = parent;                                             \
+  RB_LEFT(elm, field) = RB_RIGHT(elm, field) = NULL;                          \
+  RB_COLOR(elm, field) = RB_RED;                                              \
+} while (/*CONSTCOND*/ 0)
+
+#define RB_SET_BLACKRED(black, red, field) do {                               \
+  RB_COLOR(black, field) = RB_BLACK;                                          \
+  RB_COLOR(red, field) = RB_RED;                                              \
+} while (/*CONSTCOND*/ 0)
+
+#ifndef RB_AUGMENT
+#define RB_AUGMENT(x)  do {} while (0)
+#endif
+
+#define RB_ROTATE_LEFT(head, elm, tmp, field) do {                            \
+  (tmp) = RB_RIGHT(elm, field);                                               \
+  if ((RB_RIGHT(elm, field) = RB_LEFT(tmp, field)) != NULL) {                 \
+    RB_PARENT(RB_LEFT(tmp, field), field) = (elm);                            \
+  }                                                                           \
+  RB_AUGMENT(elm);                                                            \
+  if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field)) != NULL) {              \
+    if ((elm) == RB_LEFT(RB_PARENT(elm, field), field))                       \
+      RB_LEFT(RB_PARENT(elm, field), field) = (tmp);                          \
+    else                                                                      \
+      RB_RIGHT(RB_PARENT(elm, field), field) = (tmp);                         \
+  } else                                                                      \
+    (head)->rbh_root = (tmp);                                                 \
+  RB_LEFT(tmp, field) = (elm);                                                \
+  RB_PARENT(elm, field) = (tmp);                                              \
+  RB_AUGMENT(tmp);                                                            \
+  if ((RB_PARENT(tmp, field)))                                                \
+    RB_AUGMENT(RB_PARENT(tmp, field));                                        \
+} while (/*CONSTCOND*/ 0)
+
+#define RB_ROTATE_RIGHT(head, elm, tmp, field) do {                           \
+  (tmp) = RB_LEFT(elm, field);                                                \
+  if ((RB_LEFT(elm, field) = RB_RIGHT(tmp, field)) != NULL) {                 \
+    RB_PARENT(RB_RIGHT(tmp, field), field) = (elm);                           \
+  }                                                                           \
+  RB_AUGMENT(elm);                                                            \
+  if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field)) != NULL) {              \
+    if ((elm) == RB_LEFT(RB_PARENT(elm, field), field))                       \
+      RB_LEFT(RB_PARENT(elm, field), field) = (tmp);                          \
+    else                                                                      \
+      RB_RIGHT(RB_PARENT(elm, field), field) = (tmp);                         \
+  } else                                                                      \
+    (head)->rbh_root = (tmp);                                                 \
+  RB_RIGHT(tmp, field) = (elm);                                               \
+  RB_PARENT(elm, field) = (tmp);                                              \
+  RB_AUGMENT(tmp);                                                            \
+  if ((RB_PARENT(tmp, field)))                                                \
+    RB_AUGMENT(RB_PARENT(tmp, field));                                        \
+} while (/*CONSTCOND*/ 0)
+
+/* Generates prototypes and inline functions */
+#define  RB_PROTOTYPE(name, type, field, cmp)                                 \
+  RB_PROTOTYPE_INTERNAL(name, type, field, cmp,)
+#define  RB_PROTOTYPE_STATIC(name, type, field, cmp)                          \
+  RB_PROTOTYPE_INTERNAL(name, type, field, cmp, UV__UNUSED static)
+#define RB_PROTOTYPE_INTERNAL(name, type, field, cmp, attr)                   \
+attr void name##_RB_INSERT_COLOR(struct name *, struct type *);               \
+attr void name##_RB_REMOVE_COLOR(struct name *, struct type *, struct type *);\
+attr struct type *name##_RB_REMOVE(struct name *, struct type *);             \
+attr struct type *name##_RB_INSERT(struct name *, struct type *);             \
+attr struct type *name##_RB_FIND(struct name *, struct type *);               \
+attr struct type *name##_RB_NFIND(struct name *, struct type *);              \
+attr struct type *name##_RB_NEXT(struct type *);                              \
+attr struct type *name##_RB_PREV(struct type *);                              \
+attr struct type *name##_RB_MINMAX(struct name *, int);                       \
+                                                                              \
+
+/* Main rb operation.
+ * Moves node close to the key of elm to top
+ */
+#define  RB_GENERATE(name, type, field, cmp)                                  \
+  RB_GENERATE_INTERNAL(name, type, field, cmp,)
+#define  RB_GENERATE_STATIC(name, type, field, cmp)                           \
+  RB_GENERATE_INTERNAL(name, type, field, cmp, UV__UNUSED static)
+#define RB_GENERATE_INTERNAL(name, type, field, cmp, attr)                    \
+attr void                                                                     \
+name##_RB_INSERT_COLOR(struct name *head, struct type *elm)                   \
+{                                                                             \
+  struct type *parent, *gparent, *tmp;                                        \
+  while ((parent = RB_PARENT(elm, field)) != NULL &&                          \
+      RB_COLOR(parent, field) == RB_RED) {                                    \
+    gparent = RB_PARENT(parent, field);                                       \
+    if (parent == RB_LEFT(gparent, field)) {                                  \
+      tmp = RB_RIGHT(gparent, field);                                         \
+      if (tmp && RB_COLOR(tmp, field) == RB_RED) {                            \
+        RB_COLOR(tmp, field) = RB_BLACK;                                      \
+        RB_SET_BLACKRED(parent, gparent, field);                              \
+        elm = gparent;                                                        \
+        continue;                                                             \
+      }                                                                       \
+      if (RB_RIGHT(parent, field) == elm) {                                   \
+        RB_ROTATE_LEFT(head, parent, tmp, field);                             \
+        tmp = parent;                                                         \
+        parent = elm;                                                         \
+        elm = tmp;                                                            \
+      }                                                                       \
+      RB_SET_BLACKRED(parent, gparent, field);                                \
+      RB_ROTATE_RIGHT(head, gparent, tmp, field);                             \
+    } else {                                                                  \
+      tmp = RB_LEFT(gparent, field);                                          \
+      if (tmp && RB_COLOR(tmp, field) == RB_RED) {                            \
+        RB_COLOR(tmp, field) = RB_BLACK;                                      \
+        RB_SET_BLACKRED(parent, gparent, field);                              \
+        elm = gparent;                                                        \
+        continue;                                                             \
+      }                                                                       \
+      if (RB_LEFT(parent, field) == elm) {                                    \
+        RB_ROTATE_RIGHT(head, parent, tmp, field);                            \
+        tmp = parent;                                                         \
+        parent = elm;                                                         \
+        elm = tmp;                                                            \
+      }                                                                       \
+      RB_SET_BLACKRED(parent, gparent, field);                                \
+      RB_ROTATE_LEFT(head, gparent, tmp, field);                              \
+    }                                                                         \
+  }                                                                           \
+  RB_COLOR(head->rbh_root, field) = RB_BLACK;                                 \
+}                                                                             \
+                                                                              \
+attr void                                                                     \
+name##_RB_REMOVE_COLOR(struct name *head, struct type *parent,                \
+    struct type *elm)                                                         \
+{                                                                             \
+  struct type *tmp;                                                           \
+  while ((elm == NULL || RB_COLOR(elm, field) == RB_BLACK) &&                 \
+      elm != RB_ROOT(head)) {                                                 \
+    if (RB_LEFT(parent, field) == elm) {                                      \
+      tmp = RB_RIGHT(parent, field);                                          \
+      if (RB_COLOR(tmp, field) == RB_RED) {                                   \
+        RB_SET_BLACKRED(tmp, parent, field);                                  \
+        RB_ROTATE_LEFT(head, parent, tmp, field);                             \
+        tmp = RB_RIGHT(parent, field);                                        \
+      }                                                                       \
+      if ((RB_LEFT(tmp, field) == NULL ||                                     \
+          RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&                \
+          (RB_RIGHT(tmp, field) == NULL ||                                    \
+          RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {               \
+        RB_COLOR(tmp, field) = RB_RED;                                        \
+        elm = parent;                                                         \
+        parent = RB_PARENT(elm, field);                                       \
+      } else {                                                                \
+        if (RB_RIGHT(tmp, field) == NULL ||                                   \
+            RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK) {              \
+          struct type *oleft;                                                 \
+          if ((oleft = RB_LEFT(tmp, field))                                   \
+              != NULL)                                                        \
+            RB_COLOR(oleft, field) = RB_BLACK;                                \
+          RB_COLOR(tmp, field) = RB_RED;                                      \
+          RB_ROTATE_RIGHT(head, tmp, oleft, field);                           \
+          tmp = RB_RIGHT(parent, field);                                      \
+        }                                                                     \
+        RB_COLOR(tmp, field) = RB_COLOR(parent, field);                       \
+        RB_COLOR(parent, field) = RB_BLACK;                                   \
+        if (RB_RIGHT(tmp, field))                                             \
+          RB_COLOR(RB_RIGHT(tmp, field), field) = RB_BLACK;                   \
+        RB_ROTATE_LEFT(head, parent, tmp, field);                             \
+        elm = RB_ROOT(head);                                                  \
+        break;                                                                \
+      }                                                                       \
+    } else {                                                                  \
+      tmp = RB_LEFT(parent, field);                                           \
+      if (RB_COLOR(tmp, field) == RB_RED) {                                   \
+        RB_SET_BLACKRED(tmp, parent, field);                                  \
+        RB_ROTATE_RIGHT(head, parent, tmp, field);                            \
+        tmp = RB_LEFT(parent, field);                                         \
+      }                                                                       \
+      if ((RB_LEFT(tmp, field) == NULL ||                                     \
+          RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&                \
+          (RB_RIGHT(tmp, field) == NULL ||                                    \
+          RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {               \
+        RB_COLOR(tmp, field) = RB_RED;                                        \
+        elm = parent;                                                         \
+        parent = RB_PARENT(elm, field);                                       \
+      } else {                                                                \
+        if (RB_LEFT(tmp, field) == NULL ||                                    \
+            RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) {               \
+          struct type *oright;                                                \
+          if ((oright = RB_RIGHT(tmp, field))                                 \
+              != NULL)                                                        \
+            RB_COLOR(oright, field) = RB_BLACK;                               \
+          RB_COLOR(tmp, field) = RB_RED;                                      \
+          RB_ROTATE_LEFT(head, tmp, oright, field);                           \
+          tmp = RB_LEFT(parent, field);                                       \
+        }                                                                     \
+        RB_COLOR(tmp, field) = RB_COLOR(parent, field);                       \
+        RB_COLOR(parent, field) = RB_BLACK;                                   \
+        if (RB_LEFT(tmp, field))                                              \
+          RB_COLOR(RB_LEFT(tmp, field), field) = RB_BLACK;                    \
+        RB_ROTATE_RIGHT(head, parent, tmp, field);                            \
+        elm = RB_ROOT(head);                                                  \
+        break;                                                                \
+      }                                                                       \
+    }                                                                         \
+  }                                                                           \
+  if (elm)                                                                    \
+    RB_COLOR(elm, field) = RB_BLACK;                                          \
+}                                                                             \
+                                                                              \
+attr struct type *                                                            \
+name##_RB_REMOVE(struct name *head, struct type *elm)                         \
+{                                                                             \
+  struct type *child, *parent, *old = elm;                                    \
+  int color;                                                                  \
+  if (RB_LEFT(elm, field) == NULL)                                            \
+    child = RB_RIGHT(elm, field);                                             \
+  else if (RB_RIGHT(elm, field) == NULL)                                      \
+    child = RB_LEFT(elm, field);                                              \
+  else {                                                                      \
+    struct type *left;                                                        \
+    elm = RB_RIGHT(elm, field);                                               \
+    while ((left = RB_LEFT(elm, field)) != NULL)                              \
+      elm = left;                                                             \
+    child = RB_RIGHT(elm, field);                                             \
+    parent = RB_PARENT(elm, field);                                           \
+    color = RB_COLOR(elm, field);                                             \
+    if (child)                                                                \
+      RB_PARENT(child, field) = parent;                                       \
+    if (parent) {                                                             \
+      if (RB_LEFT(parent, field) == elm)                                      \
+        RB_LEFT(parent, field) = child;                                       \
+      else                                                                    \
+        RB_RIGHT(parent, field) = child;                                      \
+      RB_AUGMENT(parent);                                                     \
+    } else                                                                    \
+      RB_ROOT(head) = child;                                                  \
+    if (RB_PARENT(elm, field) == old)                                         \
+      parent = elm;                                                           \
+    (elm)->field = (old)->field;                                              \
+    if (RB_PARENT(old, field)) {                                              \
+      if (RB_LEFT(RB_PARENT(old, field), field) == old)                       \
+        RB_LEFT(RB_PARENT(old, field), field) = elm;                          \
+      else                                                                    \
+        RB_RIGHT(RB_PARENT(old, field), field) = elm;                         \
+      RB_AUGMENT(RB_PARENT(old, field));                                      \
+    } else                                                                    \
+      RB_ROOT(head) = elm;                                                    \
+    RB_PARENT(RB_LEFT(old, field), field) = elm;                              \
+    if (RB_RIGHT(old, field))                                                 \
+      RB_PARENT(RB_RIGHT(old, field), field) = elm;                           \
+    if (parent) {                                                             \
+      left = parent;                                                          \
+      do {                                                                    \
+        RB_AUGMENT(left);                                                     \
+      } while ((left = RB_PARENT(left, field)) != NULL);                      \
+    }                                                                         \
+    goto color;                                                               \
+  }                                                                           \
+  parent = RB_PARENT(elm, field);                                             \
+  color = RB_COLOR(elm, field);                                               \
+  if (child)                                                                  \
+    RB_PARENT(child, field) = parent;                                         \
+  if (parent) {                                                               \
+    if (RB_LEFT(parent, field) == elm)                                        \
+      RB_LEFT(parent, field) = child;                                         \
+    else                                                                      \
+      RB_RIGHT(parent, field) = child;                                        \
+    RB_AUGMENT(parent);                                                       \
+  } else                                                                      \
+    RB_ROOT(head) = child;                                                    \
+color:                                                                        \
+  if (color == RB_BLACK)                                                      \
+    name##_RB_REMOVE_COLOR(head, parent, child);                              \
+  return (old);                                                               \
+}                                                                             \
+                                                                              \
+/* Inserts a node into the RB tree */                                         \
+attr struct type *                                                            \
+name##_RB_INSERT(struct name *head, struct type *elm)                         \
+{                                                                             \
+  struct type *tmp;                                                           \
+  struct type *parent = NULL;                                                 \
+  int comp = 0;                                                               \
+  tmp = RB_ROOT(head);                                                        \
+  while (tmp) {                                                               \
+    parent = tmp;                                                             \
+    comp = (cmp)(elm, parent);                                                \
+    if (comp < 0)                                                             \
+      tmp = RB_LEFT(tmp, field);                                              \
+    else if (comp > 0)                                                        \
+      tmp = RB_RIGHT(tmp, field);                                             \
+    else                                                                      \
+      return (tmp);                                                           \
+  }                                                                           \
+  RB_SET(elm, parent, field);                                                 \
+  if (parent != NULL) {                                                       \
+    if (comp < 0)                                                             \
+      RB_LEFT(parent, field) = elm;                                           \
+    else                                                                      \
+      RB_RIGHT(parent, field) = elm;                                          \
+    RB_AUGMENT(parent);                                                       \
+  } else                                                                      \
+    RB_ROOT(head) = elm;                                                      \
+  name##_RB_INSERT_COLOR(head, elm);                                          \
+  return (NULL);                                                              \
+}                                                                             \
+                                                                              \
+/* Finds the node with the same key as elm */                                 \
+attr struct type *                                                            \
+name##_RB_FIND(struct name *head, struct type *elm)                           \
+{                                                                             \
+  struct type *tmp = RB_ROOT(head);                                           \
+  int comp;                                                                   \
+  while (tmp) {                                                               \
+    comp = cmp(elm, tmp);                                                     \
+    if (comp < 0)                                                             \
+      tmp = RB_LEFT(tmp, field);                                              \
+    else if (comp > 0)                                                        \
+      tmp = RB_RIGHT(tmp, field);                                             \
+    else                                                                      \
+      return (tmp);                                                           \
+  }                                                                           \
+  return (NULL);                                                              \
+}                                                                             \
+                                                                              \
+/* Finds the first node greater than or equal to the search key */            \
+attr struct type *                                                            \
+name##_RB_NFIND(struct name *head, struct type *elm)                          \
+{                                                                             \
+  struct type *tmp = RB_ROOT(head);                                           \
+  struct type *res = NULL;                                                    \
+  int comp;                                                                   \
+  while (tmp) {                                                               \
+    comp = cmp(elm, tmp);                                                     \
+    if (comp < 0) {                                                           \
+      res = tmp;                                                              \
+      tmp = RB_LEFT(tmp, field);                                              \
+    }                                                                         \
+    else if (comp > 0)                                                        \
+      tmp = RB_RIGHT(tmp, field);                                             \
+    else                                                                      \
+      return (tmp);                                                           \
+  }                                                                           \
+  return (res);                                                               \
+}                                                                             \
+                                                                              \
+/* ARGSUSED */                                                                \
+attr struct type *                                                            \
+name##_RB_NEXT(struct type *elm)                                              \
+{                                                                             \
+  if (RB_RIGHT(elm, field)) {                                                 \
+    elm = RB_RIGHT(elm, field);                                               \
+    while (RB_LEFT(elm, field))                                               \
+      elm = RB_LEFT(elm, field);                                              \
+  } else {                                                                    \
+    if (RB_PARENT(elm, field) &&                                              \
+        (elm == RB_LEFT(RB_PARENT(elm, field), field)))                       \
+      elm = RB_PARENT(elm, field);                                            \
+    else {                                                                    \
+      while (RB_PARENT(elm, field) &&                                         \
+          (elm == RB_RIGHT(RB_PARENT(elm, field), field)))                    \
+        elm = RB_PARENT(elm, field);                                          \
+      elm = RB_PARENT(elm, field);                                            \
+    }                                                                         \
+  }                                                                           \
+  return (elm);                                                               \
+}                                                                             \
+                                                                              \
+/* ARGSUSED */                                                                \
+attr struct type *                                                            \
+name##_RB_PREV(struct type *elm)                                              \
+{                                                                             \
+  if (RB_LEFT(elm, field)) {                                                  \
+    elm = RB_LEFT(elm, field);                                                \
+    while (RB_RIGHT(elm, field))                                              \
+      elm = RB_RIGHT(elm, field);                                             \
+  } else {                                                                    \
+    if (RB_PARENT(elm, field) &&                                              \
+        (elm == RB_RIGHT(RB_PARENT(elm, field), field)))                      \
+      elm = RB_PARENT(elm, field);                                            \
+    else {                                                                    \
+      while (RB_PARENT(elm, field) &&                                         \
+          (elm == RB_LEFT(RB_PARENT(elm, field), field)))                     \
+        elm = RB_PARENT(elm, field);                                          \
+      elm = RB_PARENT(elm, field);                                            \
+    }                                                                         \
+  }                                                                           \
+  return (elm);                                                               \
+}                                                                             \
+                                                                              \
+attr struct type *                                                            \
+name##_RB_MINMAX(struct name *head, int val)                                  \
+{                                                                             \
+  struct type *tmp = RB_ROOT(head);                                           \
+  struct type *parent = NULL;                                                 \
+  while (tmp) {                                                               \
+    parent = tmp;                                                             \
+    if (val < 0)                                                              \
+      tmp = RB_LEFT(tmp, field);                                              \
+    else                                                                      \
+      tmp = RB_RIGHT(tmp, field);                                             \
+  }                                                                           \
+  return (parent);                                                            \
+}
+
+#define RB_NEGINF   -1
+#define RB_INF      1
+
+#define RB_INSERT(name, x, y)   name##_RB_INSERT(x, y)
+#define RB_REMOVE(name, x, y)   name##_RB_REMOVE(x, y)
+#define RB_FIND(name, x, y)     name##_RB_FIND(x, y)
+#define RB_NFIND(name, x, y)    name##_RB_NFIND(x, y)
+#define RB_NEXT(name, x, y)     name##_RB_NEXT(y)
+#define RB_PREV(name, x, y)     name##_RB_PREV(y)
+#define RB_MIN(name, x)         name##_RB_MINMAX(x, RB_NEGINF)
+#define RB_MAX(name, x)         name##_RB_MINMAX(x, RB_INF)
+
+#define RB_FOREACH(x, name, head)                                             \
+  for ((x) = RB_MIN(name, head);                                              \
+       (x) != NULL;                                                           \
+       (x) = name##_RB_NEXT(x))
+
+#define RB_FOREACH_FROM(x, name, y)                                           \
+  for ((x) = (y);                                                             \
+      ((x) != NULL) && ((y) = name##_RB_NEXT(x), (x) != NULL);                \
+       (x) = (y))
+
+#define RB_FOREACH_SAFE(x, name, head, y)                                     \
+  for ((x) = RB_MIN(name, head);                                              \
+      ((x) != NULL) && ((y) = name##_RB_NEXT(x), (x) != NULL);                \
+       (x) = (y))
+
+#define RB_FOREACH_REVERSE(x, name, head)                                     \
+  for ((x) = RB_MAX(name, head);                                              \
+       (x) != NULL;                                                           \
+       (x) = name##_RB_PREV(x))
+
+#define RB_FOREACH_REVERSE_FROM(x, name, y)                                   \
+  for ((x) = (y);                                                             \
+      ((x) != NULL) && ((y) = name##_RB_PREV(x), (x) != NULL);                \
+       (x) = (y))
+
+#define RB_FOREACH_REVERSE_SAFE(x, name, head, y)                             \
+  for ((x) = RB_MAX(name, head);                                              \
+      ((x) != NULL) && ((y) = name##_RB_PREV(x), (x) != NULL);                \
+       (x) = (y))
+
+#endif  /* UV_TREE_H_ */
diff --git a/wpinet/src/main/native/thirdparty/libuv/include/uv/unix.h b/wpinet/src/main/native/thirdparty/libuv/include/uv/unix.h
new file mode 100644
index 0000000..256fef3
--- /dev/null
+++ b/wpinet/src/main/native/thirdparty/libuv/include/uv/unix.h
@@ -0,0 +1,497 @@
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#ifndef UV_UNIX_H
+#define UV_UNIX_H
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <dirent.h>
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <arpa/inet.h>
+#include <netdb.h>  /* MAXHOSTNAMELEN on Solaris */
+
+#include <termios.h>
+#include <pwd.h>
+
+#if !defined(__MVS__)
+#include <semaphore.h>
+#include <sys/param.h> /* MAXHOSTNAMELEN on Linux and the BSDs */
+#endif
+#include <pthread.h>
+#include <signal.h>
+
+#include "uv/threadpool.h"
+
+#if defined(__linux__)
+# include "uv/linux.h"
+#elif defined(__APPLE__)
+# include "uv/darwin.h"
+#elif defined(__DragonFly__)       || \
+      defined(__FreeBSD__)         || \
+      defined(__FreeBSD_kernel__)  || \
+      defined(__OpenBSD__)         || \
+      defined(__NetBSD__)
+# include "uv/bsd.h"
+#elif defined(__CYGWIN__) || \
+      defined(__MSYS__)   || \
+      defined(__HAIKU__)  || \
+      defined(__QNX__)    || \
+      defined(__GNU__)
+# include "uv/posix.h"
+#endif
+
+#ifndef NI_MAXHOST
+# define NI_MAXHOST 1025
+#endif
+
+#ifndef NI_MAXSERV
+# define NI_MAXSERV 32
+#endif
+
+#ifndef UV_IO_PRIVATE_PLATFORM_FIELDS
+# define UV_IO_PRIVATE_PLATFORM_FIELDS /* empty */
+#endif
+
+struct uv__io_s;
+struct uv_loop_s;
+
+typedef void (*uv__io_cb)(struct uv_loop_s* loop,
+                          struct uv__io_s* w,
+                          unsigned int events);
+typedef struct uv__io_s uv__io_t;
+
+struct uv__io_s {
+  uv__io_cb cb;
+  void* pending_queue[2];
+  void* watcher_queue[2];
+  unsigned int pevents; /* Pending event mask i.e. mask at next tick. */
+  unsigned int events;  /* Current event mask. */
+  int fd;
+  UV_IO_PRIVATE_PLATFORM_FIELDS
+};
+
+#ifndef UV_PLATFORM_SEM_T
+# define UV_PLATFORM_SEM_T sem_t
+#endif
+
+#ifndef UV_PLATFORM_LOOP_FIELDS
+# define UV_PLATFORM_LOOP_FIELDS /* empty */
+#endif
+
+#ifndef UV_PLATFORM_FS_EVENT_FIELDS
+# define UV_PLATFORM_FS_EVENT_FIELDS /* empty */
+#endif
+
+#ifndef UV_STREAM_PRIVATE_PLATFORM_FIELDS
+# define UV_STREAM_PRIVATE_PLATFORM_FIELDS /* empty */
+#endif
+
+/* Note: May be cast to struct iovec. See writev(2). */
+typedef struct uv_buf_t {
+  char* base;
+  size_t len;
+} uv_buf_t;
+
+typedef int uv_file;
+typedef int uv_os_sock_t;
+typedef int uv_os_fd_t;
+typedef pid_t uv_pid_t;
+
+#define UV_ONCE_INIT PTHREAD_ONCE_INIT
+
+typedef pthread_once_t uv_once_t;
+typedef pthread_t uv_thread_t;
+typedef pthread_mutex_t uv_mutex_t;
+typedef pthread_rwlock_t uv_rwlock_t;
+typedef UV_PLATFORM_SEM_T uv_sem_t;
+typedef pthread_cond_t uv_cond_t;
+typedef pthread_key_t uv_key_t;
+
+/* Note: guard clauses should match uv_barrier_init's in src/unix/thread.c. */
+#if defined(_AIX) || \
+    defined(__OpenBSD__) || \
+    !defined(PTHREAD_BARRIER_SERIAL_THREAD)
+/* TODO(bnoordhuis) Merge into uv_barrier_t in v2. */
+struct _uv_barrier {
+  uv_mutex_t mutex;
+  uv_cond_t cond;
+  unsigned threshold;
+  unsigned in;
+  unsigned out;
+};
+
+typedef struct {
+  struct _uv_barrier* b;
+# if defined(PTHREAD_BARRIER_SERIAL_THREAD)
+  /* TODO(bnoordhuis) Remove padding in v2. */
+  char pad[sizeof(pthread_barrier_t) - sizeof(struct _uv_barrier*)];
+# endif
+} uv_barrier_t;
+#else
+typedef pthread_barrier_t uv_barrier_t;
+#endif
+
+/* Platform-specific definitions for uv_spawn support. */
+typedef gid_t uv_gid_t;
+typedef uid_t uv_uid_t;
+
+typedef struct dirent uv__dirent_t;
+
+#define UV_DIR_PRIVATE_FIELDS \
+  DIR* dir;
+
+#if defined(DT_UNKNOWN)
+# define HAVE_DIRENT_TYPES
+# if defined(DT_REG)
+#  define UV__DT_FILE DT_REG
+# else
+#  define UV__DT_FILE -1
+# endif
+# if defined(DT_DIR)
+#  define UV__DT_DIR DT_DIR
+# else
+#  define UV__DT_DIR -2
+# endif
+# if defined(DT_LNK)
+#  define UV__DT_LINK DT_LNK
+# else
+#  define UV__DT_LINK -3
+# endif
+# if defined(DT_FIFO)
+#  define UV__DT_FIFO DT_FIFO
+# else
+#  define UV__DT_FIFO -4
+# endif
+# if defined(DT_SOCK)
+#  define UV__DT_SOCKET DT_SOCK
+# else
+#  define UV__DT_SOCKET -5
+# endif
+# if defined(DT_CHR)
+#  define UV__DT_CHAR DT_CHR
+# else
+#  define UV__DT_CHAR -6
+# endif
+# if defined(DT_BLK)
+#  define UV__DT_BLOCK DT_BLK
+# else
+#  define UV__DT_BLOCK -7
+# endif
+#endif
+
+/* Platform-specific definitions for uv_dlopen support. */
+#define UV_DYNAMIC /* empty */
+
+typedef struct {
+  void* handle;
+  char* errmsg;
+} uv_lib_t;
+
+#define UV_LOOP_PRIVATE_FIELDS                                                \
+  unsigned long flags;                                                        \
+  int backend_fd;                                                             \
+  void* pending_queue[2];                                                     \
+  void* watcher_queue[2];                                                     \
+  void** watchers;                                                            \
+  unsigned int nwatchers;                                                     \
+  unsigned int nfds;                                                          \
+  void* wq[2];                                                                \
+  uv_mutex_t wq_mutex;                                                        \
+  uv_async_t wq_async;                                                        \
+  uv_rwlock_t cloexec_lock;                                                   \
+  uv_handle_t* closing_handles;                                               \
+  void* process_handles[2];                                                   \
+  void* prepare_handles[2];                                                   \
+  void* check_handles[2];                                                     \
+  void* idle_handles[2];                                                      \
+  void* async_handles[2];                                                     \
+  void (*async_unused)(void);  /* TODO(bnoordhuis) Remove in libuv v2. */     \
+  uv__io_t async_io_watcher;                                                  \
+  int async_wfd;                                                              \
+  struct {                                                                    \
+    void* min;                                                                \
+    unsigned int nelts;                                                       \
+  } timer_heap;                                                               \
+  uint64_t timer_counter;                                                     \
+  uint64_t time;                                                              \
+  int signal_pipefd[2];                                                       \
+  uv__io_t signal_io_watcher;                                                 \
+  uv_signal_t child_watcher;                                                  \
+  int emfile_fd;                                                              \
+  UV_PLATFORM_LOOP_FIELDS                                                     \
+
+#define UV_REQ_TYPE_PRIVATE /* empty */
+
+#define UV_REQ_PRIVATE_FIELDS  /* empty */
+
+#define UV_PRIVATE_REQ_TYPES /* empty */
+
+#define UV_WRITE_PRIVATE_FIELDS                                               \
+  void* queue[2];                                                             \
+  unsigned int write_index;                                                   \
+  uv_buf_t* bufs;                                                             \
+  unsigned int nbufs;                                                         \
+  int error;                                                                  \
+  uv_buf_t bufsml[4];                                                         \
+
+#define UV_CONNECT_PRIVATE_FIELDS                                             \
+  void* queue[2];                                                             \
+
+#define UV_SHUTDOWN_PRIVATE_FIELDS /* empty */
+
+#define UV_UDP_SEND_PRIVATE_FIELDS                                            \
+  void* queue[2];                                                             \
+  struct sockaddr_storage addr;                                               \
+  unsigned int nbufs;                                                         \
+  uv_buf_t* bufs;                                                             \
+  ssize_t status;                                                             \
+  uv_udp_send_cb send_cb;                                                     \
+  uv_buf_t bufsml[4];                                                         \
+
+#define UV_HANDLE_PRIVATE_FIELDS                                              \
+  uv_handle_t* next_closing;                                                  \
+  unsigned int flags;                                                         \
+
+#define UV_STREAM_PRIVATE_FIELDS                                              \
+  uv_connect_t *connect_req;                                                  \
+  uv_shutdown_t *shutdown_req;                                                \
+  uv__io_t io_watcher;                                                        \
+  void* write_queue[2];                                                       \
+  void* write_completed_queue[2];                                             \
+  uv_connection_cb connection_cb;                                             \
+  int delayed_error;                                                          \
+  int accepted_fd;                                                            \
+  void* queued_fds;                                                           \
+  UV_STREAM_PRIVATE_PLATFORM_FIELDS                                           \
+
+#define UV_TCP_PRIVATE_FIELDS /* empty */
+
+#define UV_UDP_PRIVATE_FIELDS                                                 \
+  uv_alloc_cb alloc_cb;                                                       \
+  uv_udp_recv_cb recv_cb;                                                     \
+  uv__io_t io_watcher;                                                        \
+  void* write_queue[2];                                                       \
+  void* write_completed_queue[2];                                             \
+
+#define UV_PIPE_PRIVATE_FIELDS                                                \
+  const char* pipe_fname; /* strdup'ed */
+
+#define UV_POLL_PRIVATE_FIELDS                                                \
+  uv__io_t io_watcher;
+
+#define UV_PREPARE_PRIVATE_FIELDS                                             \
+  uv_prepare_cb prepare_cb;                                                   \
+  void* queue[2];                                                             \
+
+#define UV_CHECK_PRIVATE_FIELDS                                               \
+  uv_check_cb check_cb;                                                       \
+  void* queue[2];                                                             \
+
+#define UV_IDLE_PRIVATE_FIELDS                                                \
+  uv_idle_cb idle_cb;                                                         \
+  void* queue[2];                                                             \
+
+#define UV_ASYNC_PRIVATE_FIELDS                                               \
+  uv_async_cb async_cb;                                                       \
+  void* queue[2];                                                             \
+  int pending;                                                                \
+
+#define UV_TIMER_PRIVATE_FIELDS                                               \
+  uv_timer_cb timer_cb;                                                       \
+  void* heap_node[3];                                                         \
+  uint64_t timeout;                                                           \
+  uint64_t repeat;                                                            \
+  uint64_t start_id;
+
+#define UV_GETADDRINFO_PRIVATE_FIELDS                                         \
+  struct uv__work work_req;                                                   \
+  uv_getaddrinfo_cb cb;                                                       \
+  struct addrinfo* hints;                                                     \
+  char* hostname;                                                             \
+  char* service;                                                              \
+  struct addrinfo* addrinfo;                                                  \
+  int retcode;
+
+#define UV_GETNAMEINFO_PRIVATE_FIELDS                                         \
+  struct uv__work work_req;                                                   \
+  uv_getnameinfo_cb getnameinfo_cb;                                           \
+  struct sockaddr_storage storage;                                            \
+  int flags;                                                                  \
+  char host[NI_MAXHOST];                                                      \
+  char service[NI_MAXSERV];                                                   \
+  int retcode;
+
+#define UV_PROCESS_PRIVATE_FIELDS                                             \
+  void* queue[2];                                                             \
+  int status;                                                                 \
+
+#define UV_FS_PRIVATE_FIELDS                                                  \
+  const char *new_path;                                                       \
+  uv_file file;                                                               \
+  int flags;                                                                  \
+  mode_t mode;                                                                \
+  unsigned int nbufs;                                                         \
+  uv_buf_t* bufs;                                                             \
+  off_t off;                                                                  \
+  uv_uid_t uid;                                                               \
+  uv_gid_t gid;                                                               \
+  double atime;                                                               \
+  double mtime;                                                               \
+  struct uv__work work_req;                                                   \
+  uv_buf_t bufsml[4];                                                         \
+
+#define UV_WORK_PRIVATE_FIELDS                                                \
+  struct uv__work work_req;
+
+#define UV_TTY_PRIVATE_FIELDS                                                 \
+  struct termios orig_termios;                                                \
+  int mode;
+
+#define UV_SIGNAL_PRIVATE_FIELDS                                              \
+  /* RB_ENTRY(uv_signal_s) tree_entry; */                                     \
+  struct {                                                                    \
+    struct uv_signal_s* rbe_left;                                             \
+    struct uv_signal_s* rbe_right;                                            \
+    struct uv_signal_s* rbe_parent;                                           \
+    int rbe_color;                                                            \
+  } tree_entry;                                                               \
+  /* Use two counters here so we don have to fiddle with atomics. */          \
+  unsigned int caught_signals;                                                \
+  unsigned int dispatched_signals;
+
+#define UV_FS_EVENT_PRIVATE_FIELDS                                            \
+  uv_fs_event_cb cb;                                                          \
+  UV_PLATFORM_FS_EVENT_FIELDS                                                 \
+
+/* fs open() flags supported on this platform: */
+#if defined(O_APPEND)
+# define UV_FS_O_APPEND       O_APPEND
+#else
+# define UV_FS_O_APPEND       0
+#endif
+#if defined(O_CREAT)
+# define UV_FS_O_CREAT        O_CREAT
+#else
+# define UV_FS_O_CREAT        0
+#endif
+
+#if defined(__linux__) && defined(__arm__)
+# define UV_FS_O_DIRECT       0x10000
+#elif defined(__linux__) && defined(__m68k__)
+# define UV_FS_O_DIRECT       0x10000
+#elif defined(__linux__) && defined(__mips__)
+# define UV_FS_O_DIRECT       0x08000
+#elif defined(__linux__) && defined(__powerpc__)
+# define UV_FS_O_DIRECT       0x20000
+#elif defined(__linux__) && defined(__s390x__)
+# define UV_FS_O_DIRECT       0x04000
+#elif defined(__linux__) && defined(__x86_64__)
+# define UV_FS_O_DIRECT       0x04000
+#elif defined(O_DIRECT)
+# define UV_FS_O_DIRECT       O_DIRECT
+#else
+# define UV_FS_O_DIRECT       0
+#endif
+
+#if defined(O_DIRECTORY)
+# define UV_FS_O_DIRECTORY    O_DIRECTORY
+#else
+# define UV_FS_O_DIRECTORY    0
+#endif
+#if defined(O_DSYNC)
+# define UV_FS_O_DSYNC        O_DSYNC
+#else
+# define UV_FS_O_DSYNC        0
+#endif
+#if defined(O_EXCL)
+# define UV_FS_O_EXCL         O_EXCL
+#else
+# define UV_FS_O_EXCL         0
+#endif
+#if defined(O_EXLOCK)
+# define UV_FS_O_EXLOCK       O_EXLOCK
+#else
+# define UV_FS_O_EXLOCK       0
+#endif
+#if defined(O_NOATIME)
+# define UV_FS_O_NOATIME      O_NOATIME
+#else
+# define UV_FS_O_NOATIME      0
+#endif
+#if defined(O_NOCTTY)
+# define UV_FS_O_NOCTTY       O_NOCTTY
+#else
+# define UV_FS_O_NOCTTY       0
+#endif
+#if defined(O_NOFOLLOW)
+# define UV_FS_O_NOFOLLOW     O_NOFOLLOW
+#else
+# define UV_FS_O_NOFOLLOW     0
+#endif
+#if defined(O_NONBLOCK)
+# define UV_FS_O_NONBLOCK     O_NONBLOCK
+#else
+# define UV_FS_O_NONBLOCK     0
+#endif
+#if defined(O_RDONLY)
+# define UV_FS_O_RDONLY       O_RDONLY
+#else
+# define UV_FS_O_RDONLY       0
+#endif
+#if defined(O_RDWR)
+# define UV_FS_O_RDWR         O_RDWR
+#else
+# define UV_FS_O_RDWR         0
+#endif
+#if defined(O_SYMLINK)
+# define UV_FS_O_SYMLINK      O_SYMLINK
+#else
+# define UV_FS_O_SYMLINK      0
+#endif
+#if defined(O_SYNC)
+# define UV_FS_O_SYNC         O_SYNC
+#else
+# define UV_FS_O_SYNC         0
+#endif
+#if defined(O_TRUNC)
+# define UV_FS_O_TRUNC        O_TRUNC
+#else
+# define UV_FS_O_TRUNC        0
+#endif
+#if defined(O_WRONLY)
+# define UV_FS_O_WRONLY       O_WRONLY
+#else
+# define UV_FS_O_WRONLY       0
+#endif
+
+/* fs open() flags supported on other platforms: */
+#define UV_FS_O_FILEMAP       0
+#define UV_FS_O_RANDOM        0
+#define UV_FS_O_SHORT_LIVED   0
+#define UV_FS_O_SEQUENTIAL    0
+#define UV_FS_O_TEMPORARY     0
+
+#endif /* UV_UNIX_H */
diff --git a/wpinet/src/main/native/thirdparty/libuv/include/uv/version.h b/wpinet/src/main/native/thirdparty/libuv/include/uv/version.h
new file mode 100644
index 0000000..9c9d292
--- /dev/null
+++ b/wpinet/src/main/native/thirdparty/libuv/include/uv/version.h
@@ -0,0 +1,43 @@
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#ifndef UV_VERSION_H
+#define UV_VERSION_H
+
+ /*
+ * Versions with the same major number are ABI stable. API is allowed to
+ * evolve between minor releases, but only in a backwards compatible way.
+ * Make sure you update the -soname directives in configure.ac
+ * whenever you bump UV_VERSION_MAJOR or UV_VERSION_MINOR (but
+ * not UV_VERSION_PATCH.)
+ */
+
+#define UV_VERSION_MAJOR 1
+#define UV_VERSION_MINOR 44
+#define UV_VERSION_PATCH 2
+#define UV_VERSION_IS_RELEASE 1
+#define UV_VERSION_SUFFIX ""
+
+#define UV_VERSION_HEX  ((UV_VERSION_MAJOR << 16) | \
+                         (UV_VERSION_MINOR <<  8) | \
+                         (UV_VERSION_PATCH))
+
+#endif /* UV_VERSION_H */
diff --git a/wpinet/src/main/native/thirdparty/libuv/include/uv/win.h b/wpinet/src/main/native/thirdparty/libuv/include/uv/win.h
new file mode 100644
index 0000000..0a33366
--- /dev/null
+++ b/wpinet/src/main/native/thirdparty/libuv/include/uv/win.h
@@ -0,0 +1,698 @@
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#ifndef _WIN32_WINNT
+# define _WIN32_WINNT   0x0600
+#endif
+
+#if !defined(_SSIZE_T_) && !defined(_SSIZE_T_DEFINED)
+typedef intptr_t ssize_t;
+# define SSIZE_MAX INTPTR_MAX
+# define _SSIZE_T_
+# define _SSIZE_T_DEFINED
+#endif
+
+#include <winsock2.h>
+
+#if defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR)
+typedef struct pollfd {
+  SOCKET fd;
+  short  events;
+  short  revents;
+} WSAPOLLFD, *PWSAPOLLFD, *LPWSAPOLLFD;
+#endif
+
+#ifndef LOCALE_INVARIANT
+# define LOCALE_INVARIANT 0x007f
+#endif
+
+#include <mswsock.h>
+// Disable the typedef in mstcpip.h of MinGW.
+#define _TCP_INITIAL_RTO_PARAMETERS _TCP_INITIAL_RTO_PARAMETERS__AVOID
+#define TCP_INITIAL_RTO_PARAMETERS TCP_INITIAL_RTO_PARAMETERS__AVOID
+#define PTCP_INITIAL_RTO_PARAMETERS PTCP_INITIAL_RTO_PARAMETERS__AVOID
+#include <ws2tcpip.h>
+#undef _TCP_INITIAL_RTO_PARAMETERS
+#undef TCP_INITIAL_RTO_PARAMETERS
+#undef PTCP_INITIAL_RTO_PARAMETERS
+#include <windows.h>
+
+#include <process.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+
+#include <stdint.h>
+
+#include "uv/tree.h"
+#include "uv/threadpool.h"
+
+#define MAX_PIPENAME_LEN 256
+
+#ifndef S_IFLNK
+# define S_IFLNK 0xA000
+#endif
+
+/* Additional signals supported by uv_signal and or uv_kill. The CRT defines
+ * the following signals already:
+ *
+ *   #define SIGINT           2
+ *   #define SIGILL           4
+ *   #define SIGABRT_COMPAT   6
+ *   #define SIGFPE           8
+ *   #define SIGSEGV         11
+ *   #define SIGTERM         15
+ *   #define SIGBREAK        21
+ *   #define SIGABRT         22
+ *
+ * The additional signals have values that are common on other Unix
+ * variants (Linux and Darwin)
+ */
+#define SIGHUP                1
+#define SIGKILL               9
+#define SIGWINCH             28
+
+/* Redefine NSIG to take SIGWINCH into consideration */
+#if defined(NSIG) && NSIG <= SIGWINCH
+# undef NSIG
+#endif
+#ifndef NSIG
+# define NSIG SIGWINCH + 1
+#endif
+
+/* The CRT defines SIGABRT_COMPAT as 6, which equals SIGABRT on many unix-like
+ * platforms. However MinGW doesn't define it, so we do. */
+#ifndef SIGABRT_COMPAT
+# define SIGABRT_COMPAT       6
+#endif
+
+/*
+ * Guids and typedefs for winsock extension functions
+ * Mingw32 doesn't have these :-(
+ */
+#ifndef WSAID_ACCEPTEX
+# define WSAID_ACCEPTEX                                                       \
+         {0xb5367df1, 0xcbac, 0x11cf,                                         \
+         {0x95, 0xca, 0x00, 0x80, 0x5f, 0x48, 0xa1, 0x92}}
+
+# define WSAID_CONNECTEX                                                      \
+         {0x25a207b9, 0xddf3, 0x4660,                                         \
+         {0x8e, 0xe9, 0x76, 0xe5, 0x8c, 0x74, 0x06, 0x3e}}
+
+# define WSAID_GETACCEPTEXSOCKADDRS                                           \
+         {0xb5367df2, 0xcbac, 0x11cf,                                         \
+         {0x95, 0xca, 0x00, 0x80, 0x5f, 0x48, 0xa1, 0x92}}
+
+# define WSAID_DISCONNECTEX                                                   \
+         {0x7fda2e11, 0x8630, 0x436f,                                         \
+         {0xa0, 0x31, 0xf5, 0x36, 0xa6, 0xee, 0xc1, 0x57}}
+
+# define WSAID_TRANSMITFILE                                                   \
+         {0xb5367df0, 0xcbac, 0x11cf,                                         \
+         {0x95, 0xca, 0x00, 0x80, 0x5f, 0x48, 0xa1, 0x92}}
+
+  typedef BOOL (PASCAL *LPFN_ACCEPTEX)
+                      (SOCKET sListenSocket,
+                       SOCKET sAcceptSocket,
+                       PVOID lpOutputBuffer,
+                       DWORD dwReceiveDataLength,
+                       DWORD dwLocalAddressLength,
+                       DWORD dwRemoteAddressLength,
+                       LPDWORD lpdwBytesReceived,
+                       LPOVERLAPPED lpOverlapped);
+
+  typedef BOOL (PASCAL *LPFN_CONNECTEX)
+                      (SOCKET s,
+                       const struct sockaddr* name,
+                       int namelen,
+                       PVOID lpSendBuffer,
+                       DWORD dwSendDataLength,
+                       LPDWORD lpdwBytesSent,
+                       LPOVERLAPPED lpOverlapped);
+
+  typedef void (PASCAL *LPFN_GETACCEPTEXSOCKADDRS)
+                      (PVOID lpOutputBuffer,
+                       DWORD dwReceiveDataLength,
+                       DWORD dwLocalAddressLength,
+                       DWORD dwRemoteAddressLength,
+                       LPSOCKADDR* LocalSockaddr,
+                       LPINT LocalSockaddrLength,
+                       LPSOCKADDR* RemoteSockaddr,
+                       LPINT RemoteSockaddrLength);
+
+  typedef BOOL (PASCAL *LPFN_DISCONNECTEX)
+                      (SOCKET hSocket,
+                       LPOVERLAPPED lpOverlapped,
+                       DWORD dwFlags,
+                       DWORD reserved);
+
+  typedef BOOL (PASCAL *LPFN_TRANSMITFILE)
+                      (SOCKET hSocket,
+                       HANDLE hFile,
+                       DWORD nNumberOfBytesToWrite,
+                       DWORD nNumberOfBytesPerSend,
+                       LPOVERLAPPED lpOverlapped,
+                       LPTRANSMIT_FILE_BUFFERS lpTransmitBuffers,
+                       DWORD dwFlags);
+
+  typedef PVOID RTL_SRWLOCK;
+  typedef RTL_SRWLOCK SRWLOCK, *PSRWLOCK;
+#endif
+
+typedef int (WSAAPI* LPFN_WSARECV)
+            (SOCKET socket,
+             LPWSABUF buffers,
+             DWORD buffer_count,
+             LPDWORD bytes,
+             LPDWORD flags,
+             LPWSAOVERLAPPED overlapped,
+             LPWSAOVERLAPPED_COMPLETION_ROUTINE completion_routine);
+
+typedef int (WSAAPI* LPFN_WSARECVFROM)
+            (SOCKET socket,
+             LPWSABUF buffers,
+             DWORD buffer_count,
+             LPDWORD bytes,
+             LPDWORD flags,
+             struct sockaddr* addr,
+             LPINT addr_len,
+             LPWSAOVERLAPPED overlapped,
+             LPWSAOVERLAPPED_COMPLETION_ROUTINE completion_routine);
+
+#pragma warning(push)
+#pragma warning(disable : 28251)
+
+#ifndef _NTDEF_
+  typedef LONG NTSTATUS;
+  typedef NTSTATUS *PNTSTATUS;
+#endif
+
+#pragma warning(pop)
+
+#ifndef RTL_CONDITION_VARIABLE_INIT
+  typedef PVOID CONDITION_VARIABLE, *PCONDITION_VARIABLE;
+#endif
+
+typedef struct _AFD_POLL_HANDLE_INFO {
+  HANDLE Handle;
+  ULONG Events;
+  NTSTATUS Status;
+} AFD_POLL_HANDLE_INFO, *PAFD_POLL_HANDLE_INFO;
+
+typedef struct _AFD_POLL_INFO {
+  LARGE_INTEGER Timeout;
+  ULONG NumberOfHandles;
+  ULONG Exclusive;
+  AFD_POLL_HANDLE_INFO Handles[1];
+} AFD_POLL_INFO, *PAFD_POLL_INFO;
+
+#define UV_MSAFD_PROVIDER_COUNT 4
+
+
+/**
+ * It should be possible to cast uv_buf_t[] to WSABUF[]
+ * see http://msdn.microsoft.com/en-us/library/ms741542(v=vs.85).aspx
+ */
+typedef struct uv_buf_t {
+  ULONG len;
+  char* base;
+} uv_buf_t;
+
+typedef int uv_file;
+typedef SOCKET uv_os_sock_t;
+typedef HANDLE uv_os_fd_t;
+typedef int uv_pid_t;
+
+typedef HANDLE uv_thread_t;
+
+typedef HANDLE uv_sem_t;
+
+typedef CRITICAL_SECTION uv_mutex_t;
+
+/* This condition variable implementation is based on the SetEvent solution
+ * (section 3.2) at http://www.cs.wustl.edu/~schmidt/win32-cv-1.html
+ * We could not use the SignalObjectAndWait solution (section 3.4) because
+ * it want the 2nd argument (type uv_mutex_t) of uv_cond_wait() and
+ * uv_cond_timedwait() to be HANDLEs, but we use CRITICAL_SECTIONs.
+ */
+
+typedef union {
+  CONDITION_VARIABLE cond_var;
+  struct {
+    unsigned int waiters_count;
+    CRITICAL_SECTION waiters_count_lock;
+    HANDLE signal_event;
+    HANDLE broadcast_event;
+  } unused_; /* TODO: retained for ABI compatibility; remove me in v2.x. */
+} uv_cond_t;
+
+typedef struct {
+  SRWLOCK read_write_lock_;
+  /* TODO: retained for ABI compatibility; remove me in v2.x */
+#ifdef _WIN64
+  unsigned char padding_[72];
+#else
+  unsigned char padding_[44];
+#endif
+} uv_rwlock_t;
+
+typedef struct {
+  unsigned int n;
+  unsigned int count;
+  uv_mutex_t mutex;
+  uv_sem_t turnstile1;
+  uv_sem_t turnstile2;
+} uv_barrier_t;
+
+typedef struct {
+  DWORD tls_index;
+} uv_key_t;
+
+#define UV_ONCE_INIT { 0, NULL }
+
+typedef struct uv_once_s {
+  unsigned char ran;
+  HANDLE event;
+} uv_once_t;
+
+/* Platform-specific definitions for uv_spawn support. */
+typedef unsigned char uv_uid_t;
+typedef unsigned char uv_gid_t;
+
+typedef struct uv__dirent_s {
+  int d_type;
+  char d_name[1];
+} uv__dirent_t;
+
+#define UV_DIR_PRIVATE_FIELDS \
+  HANDLE dir_handle;          \
+  WIN32_FIND_DATAW find_data; \
+  BOOL need_find_call;
+
+#define HAVE_DIRENT_TYPES
+#define UV__DT_DIR     UV_DIRENT_DIR
+#define UV__DT_FILE    UV_DIRENT_FILE
+#define UV__DT_LINK    UV_DIRENT_LINK
+#define UV__DT_FIFO    UV_DIRENT_FIFO
+#define UV__DT_SOCKET  UV_DIRENT_SOCKET
+#define UV__DT_CHAR    UV_DIRENT_CHAR
+#define UV__DT_BLOCK   UV_DIRENT_BLOCK
+
+/* Platform-specific definitions for uv_dlopen support. */
+#define UV_DYNAMIC FAR WINAPI
+typedef struct {
+  HMODULE handle;
+  char* errmsg;
+} uv_lib_t;
+
+#define UV_LOOP_PRIVATE_FIELDS                                                \
+    /* The loop's I/O completion port */                                      \
+  HANDLE iocp;                                                                \
+  /* The current time according to the event loop. in msecs. */               \
+  uint64_t time;                                                              \
+  /* Tail of a single-linked circular queue of pending reqs. If the queue */  \
+  /* is empty, tail_ is NULL. If there is only one item, */                   \
+  /* tail_->next_req == tail_ */                                              \
+  uv_req_t* pending_reqs_tail;                                                \
+  /* Head of a single-linked list of closed handles */                        \
+  uv_handle_t* endgame_handles;                                               \
+  /* TODO(bnoordhuis) Stop heap-allocating |timer_heap| in libuv v2.x. */     \
+  void* timer_heap;                                                           \
+    /* Lists of active loop (prepare / check / idle) watchers */              \
+  uv_prepare_t* prepare_handles;                                              \
+  uv_check_t* check_handles;                                                  \
+  uv_idle_t* idle_handles;                                                    \
+  /* This pointer will refer to the prepare/check/idle handle whose */        \
+  /* callback is scheduled to be called next. This is needed to allow */      \
+  /* safe removal from one of the lists above while that list being */        \
+  /* iterated over. */                                                        \
+  uv_prepare_t* next_prepare_handle;                                          \
+  uv_check_t* next_check_handle;                                              \
+  uv_idle_t* next_idle_handle;                                                \
+  /* This handle holds the peer sockets for the fast variant of uv_poll_t */  \
+  SOCKET poll_peer_sockets[UV_MSAFD_PROVIDER_COUNT];                          \
+  /* Counter to keep track of active tcp streams */                           \
+  unsigned int active_tcp_streams;                                            \
+  /* Counter to keep track of active udp streams */                           \
+  unsigned int active_udp_streams;                                            \
+  /* Counter to started timer */                                              \
+  uint64_t timer_counter;                                                     \
+  /* Threadpool */                                                            \
+  void* wq[2];                                                                \
+  uv_mutex_t wq_mutex;                                                        \
+  uv_async_t wq_async;
+
+#define UV_REQ_TYPE_PRIVATE                                                   \
+  /* TODO: remove the req suffix */                                           \
+  UV_ACCEPT,                                                                  \
+  UV_FS_EVENT_REQ,                                                            \
+  UV_POLL_REQ,                                                                \
+  UV_PROCESS_EXIT,                                                            \
+  UV_READ,                                                                    \
+  UV_UDP_RECV,                                                                \
+  UV_WAKEUP,                                                                  \
+  UV_SIGNAL_REQ,
+
+#define UV_REQ_PRIVATE_FIELDS                                                 \
+  union {                                                                     \
+    /* Used by I/O operations */                                              \
+    struct {                                                                  \
+      OVERLAPPED overlapped;                                                  \
+      size_t queued_bytes;                                                    \
+    } io;                                                                     \
+    /* in v2, we can move these to the UV_CONNECT_PRIVATE_FIELDS */           \
+    struct {                                                                  \
+      ULONG_PTR result; /* overlapped.Internal is reused to hold the result */\
+      HANDLE pipeHandle;                                                      \
+      DWORD duplex_flags;                                                     \
+    } connect;                                                                \
+  } u;                                                                        \
+  struct uv_req_s* next_req;
+
+#define UV_WRITE_PRIVATE_FIELDS \
+  int coalesced;                \
+  uv_buf_t write_buffer;        \
+  HANDLE event_handle;          \
+  HANDLE wait_handle;
+
+#define UV_CONNECT_PRIVATE_FIELDS                                             \
+  /* empty */
+
+#define UV_SHUTDOWN_PRIVATE_FIELDS                                            \
+  /* empty */
+
+#define UV_UDP_SEND_PRIVATE_FIELDS                                            \
+  /* empty */
+
+#define UV_PRIVATE_REQ_TYPES                                                  \
+  typedef struct uv_pipe_accept_s {                                           \
+    UV_REQ_FIELDS                                                             \
+    HANDLE pipeHandle;                                                        \
+    struct uv_pipe_accept_s* next_pending;                                    \
+  } uv_pipe_accept_t;                                                         \
+                                                                              \
+  typedef struct uv_tcp_accept_s {                                            \
+    UV_REQ_FIELDS                                                             \
+    SOCKET accept_socket;                                                     \
+    char accept_buffer[sizeof(struct sockaddr_storage) * 2 + 32];             \
+    HANDLE event_handle;                                                      \
+    HANDLE wait_handle;                                                       \
+    struct uv_tcp_accept_s* next_pending;                                     \
+  } uv_tcp_accept_t;                                                          \
+                                                                              \
+  typedef struct uv_read_s {                                                  \
+    UV_REQ_FIELDS                                                             \
+    HANDLE event_handle;                                                      \
+    HANDLE wait_handle;                                                       \
+  } uv_read_t;
+
+#define uv_stream_connection_fields                                           \
+  unsigned int write_reqs_pending;                                            \
+  uv_shutdown_t* shutdown_req;
+
+#define uv_stream_server_fields                                               \
+  uv_connection_cb connection_cb;
+
+#define UV_STREAM_PRIVATE_FIELDS                                              \
+  unsigned int reqs_pending;                                                  \
+  int activecnt;                                                              \
+  uv_read_t read_req;                                                         \
+  union {                                                                     \
+    struct { uv_stream_connection_fields } conn;                              \
+    struct { uv_stream_server_fields     } serv;                              \
+  } stream;
+
+#define uv_tcp_server_fields                                                  \
+  uv_tcp_accept_t* accept_reqs;                                               \
+  unsigned int processed_accepts;                                             \
+  uv_tcp_accept_t* pending_accepts;                                           \
+  LPFN_ACCEPTEX func_acceptex;
+
+#define uv_tcp_connection_fields                                              \
+  uv_buf_t read_buffer;                                                       \
+  LPFN_CONNECTEX func_connectex;
+
+#define UV_TCP_PRIVATE_FIELDS                                                 \
+  SOCKET socket;                                                              \
+  int delayed_error;                                                          \
+  union {                                                                     \
+    struct { uv_tcp_server_fields } serv;                                     \
+    struct { uv_tcp_connection_fields } conn;                                 \
+  } tcp;
+
+#define UV_UDP_PRIVATE_FIELDS                                                 \
+  SOCKET socket;                                                              \
+  unsigned int reqs_pending;                                                  \
+  int activecnt;                                                              \
+  uv_req_t recv_req;                                                          \
+  uv_buf_t recv_buffer;                                                       \
+  struct sockaddr_storage recv_from;                                          \
+  int recv_from_len;                                                          \
+  uv_udp_recv_cb recv_cb;                                                     \
+  uv_alloc_cb alloc_cb;                                                       \
+  LPFN_WSARECV func_wsarecv;                                                  \
+  LPFN_WSARECVFROM func_wsarecvfrom;
+
+#define uv_pipe_server_fields                                                 \
+  int pending_instances;                                                      \
+  uv_pipe_accept_t* accept_reqs;                                              \
+  uv_pipe_accept_t* pending_accepts;
+
+#define uv_pipe_connection_fields                                             \
+  uv_timer_t* eof_timer;                                                      \
+  uv_write_t dummy; /* TODO: retained for ABI compat; remove this in v2.x. */ \
+  DWORD ipc_remote_pid;                                                       \
+  union {                                                                     \
+    uint32_t payload_remaining;                                               \
+    uint64_t dummy; /* TODO: retained for ABI compat; remove this in v2.x. */ \
+  } ipc_data_frame;                                                           \
+  void* ipc_xfer_queue[2];                                                    \
+  int ipc_xfer_queue_length;                                                  \
+  uv_write_t* non_overlapped_writes_tail;                                     \
+  CRITICAL_SECTION readfile_thread_lock;                                      \
+  volatile HANDLE readfile_thread_handle;
+
+#define UV_PIPE_PRIVATE_FIELDS                                                \
+  HANDLE handle;                                                              \
+  WCHAR* name;                                                                \
+  union {                                                                     \
+    struct { uv_pipe_server_fields } serv;                                    \
+    struct { uv_pipe_connection_fields } conn;                                \
+  } pipe;
+
+/* TODO: put the parser states in an union - TTY handles are always half-duplex
+ * so read-state can safely overlap write-state. */
+#define UV_TTY_PRIVATE_FIELDS                                                 \
+  HANDLE handle;                                                              \
+  union {                                                                     \
+    struct {                                                                  \
+      /* Used for readable TTY handles */                                     \
+      /* TODO: remove me in v2.x. */                                          \
+      HANDLE unused_;                                                         \
+      uv_buf_t read_line_buffer;                                              \
+      HANDLE read_raw_wait;                                                   \
+      /* Fields used for translating win keystrokes into vt100 characters */  \
+      char last_key[8];                                                       \
+      unsigned char last_key_offset;                                          \
+      unsigned char last_key_len;                                             \
+      WCHAR last_utf16_high_surrogate;                                        \
+      INPUT_RECORD last_input_record;                                         \
+    } rd;                                                                     \
+    struct {                                                                  \
+      /* Used for writable TTY handles */                                     \
+      /* utf8-to-utf16 conversion state */                                    \
+      unsigned int utf8_codepoint;                                            \
+      unsigned char utf8_bytes_left;                                          \
+      /* eol conversion state */                                              \
+      unsigned char previous_eol;                                             \
+      /* ansi parser state */                                                 \
+      unsigned short ansi_parser_state;                                       \
+      unsigned char ansi_csi_argc;                                            \
+      unsigned short ansi_csi_argv[4];                                        \
+      COORD saved_position;                                                   \
+      WORD saved_attributes;                                                  \
+    } wr;                                                                     \
+  } tty;
+
+#define UV_POLL_PRIVATE_FIELDS                                                \
+  SOCKET socket;                                                              \
+  /* Used in fast mode */                                                     \
+  SOCKET peer_socket;                                                         \
+  AFD_POLL_INFO afd_poll_info_1;                                              \
+  AFD_POLL_INFO afd_poll_info_2;                                              \
+  /* Used in fast and slow mode. */                                           \
+  uv_req_t poll_req_1;                                                        \
+  uv_req_t poll_req_2;                                                        \
+  unsigned char submitted_events_1;                                           \
+  unsigned char submitted_events_2;                                           \
+  unsigned char mask_events_1;                                                \
+  unsigned char mask_events_2;                                                \
+  unsigned char events;
+
+#define UV_TIMER_PRIVATE_FIELDS                                               \
+  void* heap_node[3];                                                         \
+  int unused;                                                                 \
+  uint64_t timeout;                                                           \
+  uint64_t repeat;                                                            \
+  uint64_t start_id;                                                          \
+  uv_timer_cb timer_cb;
+
+#define UV_ASYNC_PRIVATE_FIELDS                                               \
+  struct uv_req_s async_req;                                                  \
+  uv_async_cb async_cb;                                                       \
+  /* char to avoid alignment issues */                                        \
+  char volatile async_sent;
+
+#define UV_PREPARE_PRIVATE_FIELDS                                             \
+  uv_prepare_t* prepare_prev;                                                 \
+  uv_prepare_t* prepare_next;                                                 \
+  uv_prepare_cb prepare_cb;
+
+#define UV_CHECK_PRIVATE_FIELDS                                               \
+  uv_check_t* check_prev;                                                     \
+  uv_check_t* check_next;                                                     \
+  uv_check_cb check_cb;
+
+#define UV_IDLE_PRIVATE_FIELDS                                                \
+  uv_idle_t* idle_prev;                                                       \
+  uv_idle_t* idle_next;                                                       \
+  uv_idle_cb idle_cb;
+
+#define UV_HANDLE_PRIVATE_FIELDS                                              \
+  uv_handle_t* endgame_next;                                                  \
+  unsigned int flags;
+
+#define UV_GETADDRINFO_PRIVATE_FIELDS                                         \
+  struct uv__work work_req;                                                   \
+  uv_getaddrinfo_cb getaddrinfo_cb;                                           \
+  void* alloc;                                                                \
+  WCHAR* node;                                                                \
+  WCHAR* service;                                                             \
+  /* The addrinfoW field is used to store a pointer to the hints, and    */   \
+  /* later on to store the result of GetAddrInfoW. The final result will */   \
+  /* be converted to struct addrinfo* and stored in the addrinfo field.  */   \
+  struct addrinfoW* addrinfow;                                                \
+  struct addrinfo* addrinfo;                                                  \
+  int retcode;
+
+#define UV_GETNAMEINFO_PRIVATE_FIELDS                                         \
+  struct uv__work work_req;                                                   \
+  uv_getnameinfo_cb getnameinfo_cb;                                           \
+  struct sockaddr_storage storage;                                            \
+  int flags;                                                                  \
+  char host[NI_MAXHOST];                                                      \
+  char service[NI_MAXSERV];                                                   \
+  int retcode;
+
+#define UV_PROCESS_PRIVATE_FIELDS                                             \
+  struct uv_process_exit_s {                                                  \
+    UV_REQ_FIELDS                                                             \
+  } exit_req;                                                                 \
+  BYTE* child_stdio_buffer;                                                   \
+  int exit_signal;                                                            \
+  HANDLE wait_handle;                                                         \
+  HANDLE process_handle;                                                      \
+  volatile char exit_cb_pending;
+
+#define UV_FS_PRIVATE_FIELDS                                                  \
+  struct uv__work work_req;                                                   \
+  int flags;                                                                  \
+  DWORD sys_errno_;                                                           \
+  union {                                                                     \
+    /* TODO: remove me in 0.9. */                                             \
+    WCHAR* pathw;                                                             \
+    int fd;                                                                   \
+  } file;                                                                     \
+  union {                                                                     \
+    struct {                                                                  \
+      int mode;                                                               \
+      WCHAR* new_pathw;                                                       \
+      int file_flags;                                                         \
+      int fd_out;                                                             \
+      unsigned int nbufs;                                                     \
+      uv_buf_t* bufs;                                                         \
+      int64_t offset;                                                         \
+      uv_buf_t bufsml[4];                                                     \
+    } info;                                                                   \
+    struct {                                                                  \
+      double atime;                                                           \
+      double mtime;                                                           \
+    } time;                                                                   \
+  } fs;
+
+#define UV_WORK_PRIVATE_FIELDS                                                \
+  struct uv__work work_req;
+
+#define UV_FS_EVENT_PRIVATE_FIELDS                                            \
+  struct uv_fs_event_req_s {                                                  \
+    UV_REQ_FIELDS                                                             \
+  } req;                                                                      \
+  HANDLE dir_handle;                                                          \
+  int req_pending;                                                            \
+  uv_fs_event_cb cb;                                                          \
+  WCHAR* filew;                                                               \
+  WCHAR* short_filew;                                                         \
+  WCHAR* dirw;                                                                \
+  char* buffer;
+
+#define UV_SIGNAL_PRIVATE_FIELDS                                              \
+  RB_ENTRY(uv_signal_s) tree_entry;                                           \
+  struct uv_req_s signal_req;                                                 \
+  unsigned long pending_signum;
+
+#ifndef F_OK
+#define F_OK 0
+#endif
+#ifndef R_OK
+#define R_OK 4
+#endif
+#ifndef W_OK
+#define W_OK 2
+#endif
+#ifndef X_OK
+#define X_OK 1
+#endif
+
+/* fs open() flags supported on this platform: */
+#define UV_FS_O_APPEND       _O_APPEND
+#define UV_FS_O_CREAT        _O_CREAT
+#define UV_FS_O_EXCL         _O_EXCL
+#define UV_FS_O_FILEMAP      0x20000000
+#define UV_FS_O_RANDOM       _O_RANDOM
+#define UV_FS_O_RDONLY       _O_RDONLY
+#define UV_FS_O_RDWR         _O_RDWR
+#define UV_FS_O_SEQUENTIAL   _O_SEQUENTIAL
+#define UV_FS_O_SHORT_LIVED  _O_SHORT_LIVED
+#define UV_FS_O_TEMPORARY    _O_TEMPORARY
+#define UV_FS_O_TRUNC        _O_TRUNC
+#define UV_FS_O_WRONLY       _O_WRONLY
+
+/* fs open() flags supported on other platforms (or mapped on this platform): */
+#define UV_FS_O_DIRECT       0x02000000 /* FILE_FLAG_NO_BUFFERING */
+#define UV_FS_O_DIRECTORY    0
+#define UV_FS_O_DSYNC        0x04000000 /* FILE_FLAG_WRITE_THROUGH */
+#define UV_FS_O_EXLOCK       0x10000000 /* EXCLUSIVE SHARING MODE */
+#define UV_FS_O_NOATIME      0
+#define UV_FS_O_NOCTTY       0
+#define UV_FS_O_NOFOLLOW     0
+#define UV_FS_O_NONBLOCK     0
+#define UV_FS_O_SYMLINK      0
+#define UV_FS_O_SYNC         0x08000000 /* FILE_FLAG_WRITE_THROUGH */
diff --git a/wpinet/src/main/native/thirdparty/libuv/src/fs-poll.cpp b/wpinet/src/main/native/thirdparty/libuv/src/fs-poll.cpp
new file mode 100644
index 0000000..1a7ca70
--- /dev/null
+++ b/wpinet/src/main/native/thirdparty/libuv/src/fs-poll.cpp
@@ -0,0 +1,288 @@
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include "uv.h"
+#include "uv-common.h"
+
+#ifdef _WIN32
+#include "win/internal.h"
+#include "win/handle-inl.h"
+#define uv__make_close_pending(h) uv__want_endgame((h)->loop, (h))
+#else
+#include "unix/internal.h"
+#endif
+
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+
+
+struct poll_ctx {
+  uv_fs_poll_t* parent_handle;
+  int busy_polling;
+  unsigned int interval;
+  uint64_t start_time;
+  uv_loop_t* loop;
+  uv_fs_poll_cb poll_cb;
+  uv_timer_t timer_handle;
+  uv_fs_t fs_req; /* TODO(bnoordhuis) mark fs_req internal */
+  uv_stat_t statbuf;
+  struct poll_ctx* previous; /* context from previous start()..stop() period */
+  char path[1]; /* variable length */
+};
+
+static int statbuf_eq(const uv_stat_t* a, const uv_stat_t* b);
+static void poll_cb(uv_fs_t* req);
+static void timer_cb(uv_timer_t* timer);
+static void timer_close_cb(uv_handle_t* handle);
+
+static uv_stat_t zero_statbuf;
+
+
+int uv_fs_poll_init(uv_loop_t* loop, uv_fs_poll_t* handle) {
+  uv__handle_init(loop, (uv_handle_t*)handle, UV_FS_POLL);
+  handle->poll_ctx = NULL;
+  return 0;
+}
+
+
+int uv_fs_poll_start(uv_fs_poll_t* handle,
+                     uv_fs_poll_cb cb,
+                     const char* path,
+                     unsigned int interval) {
+  struct poll_ctx* ctx;
+  uv_loop_t* loop;
+  size_t len;
+  int err;
+
+  if (uv_is_active((uv_handle_t*)handle))
+    return 0;
+
+  loop = handle->loop;
+  len = strlen(path);
+  ctx = (struct poll_ctx*)uv__calloc(1, sizeof(*ctx) + len);
+
+  if (ctx == NULL)
+    return UV_ENOMEM;
+
+  ctx->loop = loop;
+  ctx->poll_cb = cb;
+  ctx->interval = interval ? interval : 1;
+  ctx->start_time = uv_now(loop);
+  ctx->parent_handle = handle;
+  memcpy(ctx->path, path, len + 1);
+
+  err = uv_timer_init(loop, &ctx->timer_handle);
+  if (err < 0)
+    goto error;
+
+  ctx->timer_handle.flags |= UV_HANDLE_INTERNAL;
+  uv__handle_unref(&ctx->timer_handle);
+
+  err = uv_fs_stat(loop, &ctx->fs_req, ctx->path, poll_cb);
+  if (err < 0)
+    goto error;
+
+  if (handle->poll_ctx != NULL)
+    ctx->previous = (struct poll_ctx*)handle->poll_ctx;
+  handle->poll_ctx = ctx;
+  uv__handle_start(handle);
+
+  return 0;
+
+error:
+  uv__free(ctx);
+  return err;
+}
+
+
+int uv_fs_poll_stop(uv_fs_poll_t* handle) {
+  struct poll_ctx* ctx;
+
+  if (!uv_is_active((uv_handle_t*)handle))
+    return 0;
+
+  ctx = (struct poll_ctx*)handle->poll_ctx;
+  assert(ctx != NULL);
+  assert(ctx->parent_handle == handle);
+
+  /* Close the timer if it's active. If it's inactive, there's a stat request
+   * in progress and poll_cb will take care of the cleanup.
+   */
+  if (uv_is_active((uv_handle_t*)&ctx->timer_handle))
+    uv_close((uv_handle_t*)&ctx->timer_handle, timer_close_cb);
+
+  uv__handle_stop(handle);
+
+  return 0;
+}
+
+
+int uv_fs_poll_getpath(uv_fs_poll_t* handle, char* buffer, size_t* size) {
+  struct poll_ctx* ctx;
+  size_t required_len;
+
+  if (!uv_is_active((uv_handle_t*)handle)) {
+    *size = 0;
+    return UV_EINVAL;
+  }
+
+  ctx = (struct poll_ctx*)handle->poll_ctx;
+  assert(ctx != NULL);
+
+  required_len = strlen(ctx->path);
+  if (required_len >= *size) {
+    *size = required_len + 1;
+    return UV_ENOBUFS;
+  }
+
+  memcpy(buffer, ctx->path, required_len);
+  *size = required_len;
+  buffer[required_len] = '\0';
+
+  return 0;
+}
+
+
+void uv__fs_poll_close(uv_fs_poll_t* handle) {
+  uv_fs_poll_stop(handle);
+
+  if (handle->poll_ctx == NULL)
+    uv__make_close_pending((uv_handle_t*)handle);
+}
+
+
+static void timer_cb(uv_timer_t* timer) {
+  struct poll_ctx* ctx;
+
+  ctx = container_of(timer, struct poll_ctx, timer_handle);
+  assert(ctx->parent_handle != NULL);
+  assert(ctx->parent_handle->poll_ctx == ctx);
+  ctx->start_time = uv_now(ctx->loop);
+
+  if (uv_fs_stat(ctx->loop, &ctx->fs_req, ctx->path, poll_cb))
+    abort();
+}
+
+
+static void poll_cb(uv_fs_t* req) {
+  uv_stat_t* statbuf;
+  struct poll_ctx* ctx;
+  uint64_t interval;
+  uv_fs_poll_t* handle;
+
+  ctx = container_of(req, struct poll_ctx, fs_req);
+  handle = ctx->parent_handle;
+
+  if (!uv_is_active((uv_handle_t*)handle) || uv__is_closing(handle))
+    goto out;
+
+  if (req->result != 0) {
+    if (ctx->busy_polling != req->result) {
+      ctx->poll_cb(ctx->parent_handle,
+                   req->result,
+                   &ctx->statbuf,
+                   &zero_statbuf);
+      ctx->busy_polling = req->result;
+    }
+    goto out;
+  }
+
+  statbuf = &req->statbuf;
+
+  if (ctx->busy_polling != 0)
+    if (ctx->busy_polling < 0 || !statbuf_eq(&ctx->statbuf, statbuf))
+      ctx->poll_cb(ctx->parent_handle, 0, &ctx->statbuf, statbuf);
+
+  ctx->statbuf = *statbuf;
+  ctx->busy_polling = 1;
+
+out:
+  uv_fs_req_cleanup(req);
+
+  if (!uv_is_active((uv_handle_t*)handle) || uv__is_closing(handle)) {
+    uv_close((uv_handle_t*)&ctx->timer_handle, timer_close_cb);
+    return;
+  }
+
+  /* Reschedule timer, subtract the delay from doing the stat(). */
+  interval = ctx->interval;
+  interval -= (uv_now(ctx->loop) - ctx->start_time) % interval;
+
+  if (uv_timer_start(&ctx->timer_handle, timer_cb, interval, 0))
+    abort();
+}
+
+
+static void timer_close_cb(uv_handle_t* timer) {
+  struct poll_ctx* ctx;
+  struct poll_ctx* it;
+  struct poll_ctx* last;
+  uv_fs_poll_t* handle;
+
+  ctx = container_of(timer, struct poll_ctx, timer_handle);
+  handle = ctx->parent_handle;
+  if (ctx == handle->poll_ctx) {
+    handle->poll_ctx = ctx->previous;
+    if (handle->poll_ctx == NULL && uv__is_closing(handle))
+      uv__make_close_pending((uv_handle_t*)handle);
+  } else {
+    for (last = (struct poll_ctx*)handle->poll_ctx, it = last->previous;
+         it != ctx;
+         last = it, it = it->previous) {
+      assert(last->previous != NULL);
+    }
+    last->previous = ctx->previous;
+  }
+  uv__free(ctx);
+}
+
+
+static int statbuf_eq(const uv_stat_t* a, const uv_stat_t* b) {
+  return a->st_ctim.tv_nsec == b->st_ctim.tv_nsec
+      && a->st_mtim.tv_nsec == b->st_mtim.tv_nsec
+      && a->st_birthtim.tv_nsec == b->st_birthtim.tv_nsec
+      && a->st_ctim.tv_sec == b->st_ctim.tv_sec
+      && a->st_mtim.tv_sec == b->st_mtim.tv_sec
+      && a->st_birthtim.tv_sec == b->st_birthtim.tv_sec
+      && a->st_size == b->st_size
+      && a->st_mode == b->st_mode
+      && a->st_uid == b->st_uid
+      && a->st_gid == b->st_gid
+      && a->st_ino == b->st_ino
+      && a->st_dev == b->st_dev
+      && a->st_flags == b->st_flags
+      && a->st_gen == b->st_gen;
+}
+
+
+#if defined(_WIN32)
+
+#include "win/internal.h"
+#include "win/handle-inl.h"
+
+void uv__fs_poll_endgame(uv_loop_t* loop, uv_fs_poll_t* handle) {
+  assert(handle->flags & UV_HANDLE_CLOSING);
+  assert(!(handle->flags & UV_HANDLE_CLOSED));
+  uv__handle_close(handle);
+}
+
+#endif /* _WIN32 */
diff --git a/wpinet/src/main/native/thirdparty/libuv/src/heap-inl.h b/wpinet/src/main/native/thirdparty/libuv/src/heap-inl.h
new file mode 100644
index 0000000..1e2ed60
--- /dev/null
+++ b/wpinet/src/main/native/thirdparty/libuv/src/heap-inl.h
@@ -0,0 +1,245 @@
+/* Copyright (c) 2013, Ben Noordhuis <info@bnoordhuis.nl>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef UV_SRC_HEAP_H_
+#define UV_SRC_HEAP_H_
+
+#include <stddef.h>  /* NULL */
+
+#if defined(__GNUC__)
+# define HEAP_EXPORT(declaration) __attribute__((unused)) static declaration
+#else
+# define HEAP_EXPORT(declaration) static declaration
+#endif
+
+struct heap_node {
+  struct heap_node* left;
+  struct heap_node* right;
+  struct heap_node* parent;
+};
+
+/* A binary min heap.  The usual properties hold: the root is the lowest
+ * element in the set, the height of the tree is at most log2(nodes) and
+ * it's always a complete binary tree.
+ *
+ * The heap function try hard to detect corrupted tree nodes at the cost
+ * of a minor reduction in performance.  Compile with -DNDEBUG to disable.
+ */
+struct heap {
+  struct heap_node* min;
+  unsigned int nelts;
+};
+
+/* Return non-zero if a < b. */
+typedef int (*heap_compare_fn)(const struct heap_node* a,
+                               const struct heap_node* b);
+
+/* Public functions. */
+HEAP_EXPORT(void heap_init(struct heap* heap));
+HEAP_EXPORT(struct heap_node* heap_min(const struct heap* heap));
+HEAP_EXPORT(void heap_insert(struct heap* heap,
+                             struct heap_node* newnode,
+                             heap_compare_fn less_than));
+HEAP_EXPORT(void heap_remove(struct heap* heap,
+                             struct heap_node* node,
+                             heap_compare_fn less_than));
+HEAP_EXPORT(void heap_dequeue(struct heap* heap, heap_compare_fn less_than));
+
+/* Implementation follows. */
+
+HEAP_EXPORT(void heap_init(struct heap* heap)) {
+  heap->min = NULL;
+  heap->nelts = 0;
+}
+
+HEAP_EXPORT(struct heap_node* heap_min(const struct heap* heap)) {
+  return heap->min;
+}
+
+/* Swap parent with child. Child moves closer to the root, parent moves away. */
+static void heap_node_swap(struct heap* heap,
+                           struct heap_node* parent,
+                           struct heap_node* child) {
+  struct heap_node* sibling;
+  struct heap_node t;
+
+  t = *parent;
+  *parent = *child;
+  *child = t;
+
+  parent->parent = child;
+  if (child->left == child) {
+    child->left = parent;
+    sibling = child->right;
+  } else {
+    child->right = parent;
+    sibling = child->left;
+  }
+  if (sibling != NULL)
+    sibling->parent = child;
+
+  if (parent->left != NULL)
+    parent->left->parent = parent;
+  if (parent->right != NULL)
+    parent->right->parent = parent;
+
+  if (child->parent == NULL)
+    heap->min = child;
+  else if (child->parent->left == parent)
+    child->parent->left = child;
+  else
+    child->parent->right = child;
+}
+
+HEAP_EXPORT(void heap_insert(struct heap* heap,
+                             struct heap_node* newnode,
+                             heap_compare_fn less_than)) {
+  struct heap_node** parent;
+  struct heap_node** child;
+  unsigned int path;
+  unsigned int n;
+  unsigned int k;
+
+  newnode->left = NULL;
+  newnode->right = NULL;
+  newnode->parent = NULL;
+
+  /* Calculate the path from the root to the insertion point.  This is a min
+   * heap so we always insert at the left-most free node of the bottom row.
+   */
+  path = 0;
+  for (k = 0, n = 1 + heap->nelts; n >= 2; k += 1, n /= 2)
+    path = (path << 1) | (n & 1);
+
+  /* Now traverse the heap using the path we calculated in the previous step. */
+  parent = child = &heap->min;
+  while (k > 0) {
+    parent = child;
+    if (path & 1)
+      child = &(*child)->right;
+    else
+      child = &(*child)->left;
+    path >>= 1;
+    k -= 1;
+  }
+
+  /* Insert the new node. */
+  newnode->parent = *parent;
+  *child = newnode;
+  heap->nelts += 1;
+
+  /* Walk up the tree and check at each node if the heap property holds.
+   * It's a min heap so parent < child must be true.
+   */
+  while (newnode->parent != NULL && less_than(newnode, newnode->parent))
+    heap_node_swap(heap, newnode->parent, newnode);
+}
+
+HEAP_EXPORT(void heap_remove(struct heap* heap,
+                             struct heap_node* node,
+                             heap_compare_fn less_than)) {
+  struct heap_node* smallest;
+  struct heap_node** max;
+  struct heap_node* child;
+  unsigned int path;
+  unsigned int k;
+  unsigned int n;
+
+  if (heap->nelts == 0)
+    return;
+
+  /* Calculate the path from the min (the root) to the max, the left-most node
+   * of the bottom row.
+   */
+  path = 0;
+  for (k = 0, n = heap->nelts; n >= 2; k += 1, n /= 2)
+    path = (path << 1) | (n & 1);
+
+  /* Now traverse the heap using the path we calculated in the previous step. */
+  max = &heap->min;
+  while (k > 0) {
+    if (path & 1)
+      max = &(*max)->right;
+    else
+      max = &(*max)->left;
+    path >>= 1;
+    k -= 1;
+  }
+
+  heap->nelts -= 1;
+
+  /* Unlink the max node. */
+  child = *max;
+  *max = NULL;
+
+  if (child == node) {
+    /* We're removing either the max or the last node in the tree. */
+    if (child == heap->min) {
+      heap->min = NULL;
+    }
+    return;
+  }
+
+  /* Replace the to be deleted node with the max node. */
+  child->left = node->left;
+  child->right = node->right;
+  child->parent = node->parent;
+
+  if (child->left != NULL) {
+    child->left->parent = child;
+  }
+
+  if (child->right != NULL) {
+    child->right->parent = child;
+  }
+
+  if (node->parent == NULL) {
+    heap->min = child;
+  } else if (node->parent->left == node) {
+    node->parent->left = child;
+  } else {
+    node->parent->right = child;
+  }
+
+  /* Walk down the subtree and check at each node if the heap property holds.
+   * It's a min heap so parent < child must be true.  If the parent is bigger,
+   * swap it with the smallest child.
+   */
+  for (;;) {
+    smallest = child;
+    if (child->left != NULL && less_than(child->left, smallest))
+      smallest = child->left;
+    if (child->right != NULL && less_than(child->right, smallest))
+      smallest = child->right;
+    if (smallest == child)
+      break;
+    heap_node_swap(heap, child, smallest);
+  }
+
+  /* Walk up the subtree and check that each parent is less than the node
+   * this is required, because `max` node is not guaranteed to be the
+   * actual maximum in tree
+   */
+  while (child->parent != NULL && less_than(child, child->parent))
+    heap_node_swap(heap, child->parent, child);
+}
+
+HEAP_EXPORT(void heap_dequeue(struct heap* heap, heap_compare_fn less_than)) {
+  heap_remove(heap, heap->min, less_than);
+}
+
+#undef HEAP_EXPORT
+
+#endif  /* UV_SRC_HEAP_H_ */
diff --git a/wpinet/src/main/native/thirdparty/libuv/src/idna.cpp b/wpinet/src/main/native/thirdparty/libuv/src/idna.cpp
new file mode 100644
index 0000000..36a39a0
--- /dev/null
+++ b/wpinet/src/main/native/thirdparty/libuv/src/idna.cpp
@@ -0,0 +1,315 @@
+/* Copyright (c) 2011, 2018 Ben Noordhuis <info@bnoordhuis.nl>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* Derived from https://github.com/bnoordhuis/punycode
+ * but updated to support IDNA 2008.
+ */
+
+#include "uv.h"
+#include "idna.h"
+#include <assert.h>
+#include <string.h>
+#include <limits.h> /* UINT_MAX */
+
+static unsigned uv__utf8_decode1_slow(const char** p,
+                                      const char* pe,
+                                      unsigned a) {
+  unsigned b;
+  unsigned c;
+  unsigned d;
+  unsigned min;
+
+  if (a > 0xF7)
+    return -1;
+
+  switch (pe - *p) {
+  default:
+    if (a > 0xEF) {
+      min = 0x10000;
+      a = a & 7;
+      b = (unsigned char) *(*p)++;
+      c = (unsigned char) *(*p)++;
+      d = (unsigned char) *(*p)++;
+      break;
+    }
+    /* Fall through. */
+  case 2:
+    if (a > 0xDF) {
+      min = 0x800;
+      b = 0x80 | (a & 15);
+      c = (unsigned char) *(*p)++;
+      d = (unsigned char) *(*p)++;
+      a = 0;
+      break;
+    }
+    /* Fall through. */
+  case 1:
+    if (a > 0xBF) {
+      min = 0x80;
+      b = 0x80;
+      c = 0x80 | (a & 31);
+      d = (unsigned char) *(*p)++;
+      a = 0;
+      break;
+    }
+    /* Fall through. */
+  case 0:
+    return -1;  /* Invalid continuation byte. */
+  }
+
+  if (0x80 != (0xC0 & (b ^ c ^ d)))
+    return -1;  /* Invalid sequence. */
+
+  b &= 63;
+  c &= 63;
+  d &= 63;
+  a = (a << 18) | (b << 12) | (c << 6) | d;
+
+  if (a < min)
+    return -1;  /* Overlong sequence. */
+
+  if (a > 0x10FFFF)
+    return -1;  /* Four-byte sequence > U+10FFFF. */
+
+  if (a >= 0xD800 && a <= 0xDFFF)
+    return -1;  /* Surrogate pair. */
+
+  return a;
+}
+
+unsigned uv__utf8_decode1(const char** p, const char* pe) {
+  unsigned a;
+
+  assert(*p < pe);
+
+  a = (unsigned char) *(*p)++;
+
+  if (a < 128)
+    return a;  /* ASCII, common case. */
+
+  return uv__utf8_decode1_slow(p, pe, a);
+}
+
+static int uv__idna_toascii_label(const char* s, const char* se,
+                                  char** d, char* de) {
+  static const char alphabet[] = "abcdefghijklmnopqrstuvwxyz0123456789";
+  const char* ss;
+  unsigned c = 0;
+  unsigned h;
+  unsigned k;
+  unsigned n;
+  unsigned m;
+  unsigned q;
+  unsigned t;
+  unsigned x;
+  unsigned y;
+  unsigned bias;
+  unsigned delta;
+  unsigned todo;
+  int first;
+
+  h = 0;
+  ss = s;
+  todo = 0;
+
+  /* Note: after this loop we've visited all UTF-8 characters and know
+   * they're legal so we no longer need to check for decode errors.
+   */
+  while (s < se) {
+    c = uv__utf8_decode1(&s, se);
+
+    if (c == UINT_MAX)
+      return UV_EINVAL;
+
+    if (c < 128)
+      h++;
+    else
+      todo++;
+  }
+
+  /* Only write "xn--" when there are non-ASCII characters. */
+  if (todo > 0) {
+    if (*d < de) *(*d)++ = 'x';
+    if (*d < de) *(*d)++ = 'n';
+    if (*d < de) *(*d)++ = '-';
+    if (*d < de) *(*d)++ = '-';
+  }
+
+  /* Write ASCII characters. */
+  x = 0;
+  s = ss;
+  while (s < se) {
+    c = uv__utf8_decode1(&s, se);
+    assert(c != UINT_MAX);
+
+    if (c > 127)
+      continue;
+
+    if (*d < de)
+      *(*d)++ = c;
+
+    if (++x == h)
+      break;  /* Visited all ASCII characters. */
+  }
+
+  if (todo == 0)
+    return h;
+
+  /* Only write separator when we've written ASCII characters first. */
+  if (h > 0)
+    if (*d < de)
+      *(*d)++ = '-';
+
+  n = 128;
+  bias = 72;
+  delta = 0;
+  first = 1;
+
+  while (todo > 0) {
+    m = -1;
+    s = ss;
+
+    while (s < se) {
+      c = uv__utf8_decode1(&s, se);
+      assert(c != UINT_MAX);
+
+      if (c >= n)
+        if (c < m)
+          m = c;
+    }
+
+    x = m - n;
+    y = h + 1;
+
+    if (x > ~delta / y)
+      return UV_E2BIG;  /* Overflow. */
+
+    delta += x * y;
+    n = m;
+
+    s = ss;
+    while (s < se) {
+      c = uv__utf8_decode1(&s, se);
+      assert(c != UINT_MAX);
+
+      if (c < n)
+        if (++delta == 0)
+          return UV_E2BIG;  /* Overflow. */
+
+      if (c != n)
+        continue;
+
+      for (k = 36, q = delta; /* empty */; k += 36) {
+        t = 1;
+
+        if (k > bias)
+          t = k - bias;
+
+        if (t > 26)
+          t = 26;
+
+        if (q < t)
+          break;
+
+        /* TODO(bnoordhuis) Since 1 <= t <= 26 and therefore
+         * 10 <= y <= 35, we can optimize the long division
+         * into a table-based reciprocal multiplication.
+         */
+        x = q - t;
+        y = 36 - t;  /* 10 <= y <= 35 since 1 <= t <= 26. */
+        q = x / y;
+        t = t + x % y;  /* 1 <= t <= 35 because of y. */
+
+        if (*d < de)
+          *(*d)++ = alphabet[t];
+      }
+
+      if (*d < de)
+        *(*d)++ = alphabet[q];
+
+      delta /= 2;
+
+      if (first) {
+        delta /= 350;
+        first = 0;
+      }
+
+      /* No overflow check is needed because |delta| was just
+       * divided by 2 and |delta+delta >= delta + delta/h|.
+       */
+      h++;
+      delta += delta / h;
+
+      for (bias = 0; delta > 35 * 26 / 2; bias += 36)
+        delta /= 35;
+
+      bias += 36 * delta / (delta + 38);
+      delta = 0;
+      todo--;
+    }
+
+    delta++;
+    n++;
+  }
+
+  return 0;
+}
+
+long uv__idna_toascii(const char* s, const char* se, char* d, char* de) {
+  const char* si;
+  const char* st;
+  unsigned c;
+  char* ds;
+  int rc;
+
+  ds = d;
+
+  si = s;
+  while (si < se) {
+    st = si;
+    c = uv__utf8_decode1(&si, se);
+
+    if (c == UINT_MAX)
+      return UV_EINVAL;
+
+    if (c != '.')
+      if (c != 0x3002)  /* 。 */
+        if (c != 0xFF0E)  /* . */
+          if (c != 0xFF61)  /* 。 */
+            continue;
+
+    rc = uv__idna_toascii_label(s, st, &d, de);
+
+    if (rc < 0)
+      return rc;
+
+    if (d < de)
+      *d++ = '.';
+
+    s = si;
+  }
+
+  if (s < se) {
+    rc = uv__idna_toascii_label(s, se, &d, de);
+
+    if (rc < 0)
+      return rc;
+  }
+
+  if (d < de)
+    *d++ = '\0';
+
+  return d - ds;  /* Number of bytes written. */
+}
diff --git a/wpinet/src/main/native/thirdparty/libuv/src/idna.h b/wpinet/src/main/native/thirdparty/libuv/src/idna.h
new file mode 100644
index 0000000..8e0c592
--- /dev/null
+++ b/wpinet/src/main/native/thirdparty/libuv/src/idna.h
@@ -0,0 +1,31 @@
+/* Copyright (c) 2011, 2018 Ben Noordhuis <info@bnoordhuis.nl>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef UV_SRC_IDNA_H_
+#define UV_SRC_IDNA_H_
+
+/* Decode a single codepoint. Returns the codepoint or UINT32_MAX on error.
+ * |p| is updated on success _and_ error, i.e., bad multi-byte sequences are
+ * skipped in their entirety, not just the first bad byte.
+ */
+unsigned uv__utf8_decode1(const char** p, const char* pe);
+
+/* Convert a UTF-8 domain name to IDNA 2008 / Punycode. A return value >= 0
+ * is the number of bytes written to |d|, including the trailing nul byte.
+ * A return value < 0 is a libuv error code. |s| and |d| can not overlap.
+ */
+long uv__idna_toascii(const char* s, const char* se, char* d, char* de);
+
+#endif  /* UV_SRC_IDNA_H_ */
diff --git a/wpinet/src/main/native/thirdparty/libuv/src/inet.cpp b/wpinet/src/main/native/thirdparty/libuv/src/inet.cpp
new file mode 100644
index 0000000..1b19025
--- /dev/null
+++ b/wpinet/src/main/native/thirdparty/libuv/src/inet.cpp
@@ -0,0 +1,308 @@
+/*
+ * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1996-1999 by Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#if defined(_MSC_VER) && _MSC_VER < 1600
+# include "uv/stdint-msvc2008.h"
+#else
+# include <stdint.h>
+#endif
+
+#include "uv.h"
+#include "uv-common.h"
+
+#ifdef _WIN32
+#pragma warning(disable : 6001)
+#endif
+
+#define UV__INET_ADDRSTRLEN         16
+#define UV__INET6_ADDRSTRLEN        46
+
+
+static int inet_ntop4(const unsigned char *src, char *dst, size_t size);
+static int inet_ntop6(const unsigned char *src, char *dst, size_t size);
+static int inet_pton4(const char *src, unsigned char *dst);
+static int inet_pton6(const char *src, unsigned char *dst);
+
+
+int uv_inet_ntop(int af, const void* src, char* dst, size_t size) {
+  switch (af) {
+  case AF_INET:
+    return (inet_ntop4((const unsigned char*)src, dst, size));
+  case AF_INET6:
+    return (inet_ntop6((const unsigned char*)src, dst, size));
+  default:
+    return UV_EAFNOSUPPORT;
+  }
+  /* NOTREACHED */
+}
+
+
+static int inet_ntop4(const unsigned char *src, char *dst, size_t size) {
+  static const char fmt[] = "%u.%u.%u.%u";
+  char tmp[UV__INET_ADDRSTRLEN];
+  int l;
+
+  l = snprintf(tmp, sizeof(tmp), fmt, src[0], src[1], src[2], src[3]);
+  if (l <= 0 || (size_t) l >= size) {
+    return UV_ENOSPC;
+  }
+  uv__strscpy(dst, tmp, size);
+  return 0;
+}
+
+
+static int inet_ntop6(const unsigned char *src, char *dst, size_t size) {
+  /*
+   * Note that int32_t and int16_t need only be "at least" large enough
+   * to contain a value of the specified size.  On some systems, like
+   * Crays, there is no such thing as an integer variable with 16 bits.
+   * Keep this in mind if you think this function should have been coded
+   * to use pointer overlays.  All the world's not a VAX.
+   */
+  char tmp[UV__INET6_ADDRSTRLEN], *tp;
+  struct { int base, len; } best, cur;
+  unsigned int words[sizeof(struct in6_addr) / sizeof(uint16_t)];
+  int i;
+
+  /*
+   * Preprocess:
+   *  Copy the input (bytewise) array into a wordwise array.
+   *  Find the longest run of 0x00's in src[] for :: shorthanding.
+   */
+  memset(words, '\0', sizeof words);
+  for (i = 0; i < (int) sizeof(struct in6_addr); i++)
+    words[i / 2] |= (src[i] << ((1 - (i % 2)) << 3));
+  best.base = -1;
+  best.len = 0;
+  cur.base = -1;
+  cur.len = 0;
+  for (i = 0; i < (int) ARRAY_SIZE(words); i++) {
+    if (words[i] == 0) {
+      if (cur.base == -1)
+        cur.base = i, cur.len = 1;
+      else
+        cur.len++;
+    } else {
+      if (cur.base != -1) {
+        if (best.base == -1 || cur.len > best.len)
+          best = cur;
+        cur.base = -1;
+      }
+    }
+  }
+  if (cur.base != -1) {
+    if (best.base == -1 || cur.len > best.len)
+      best = cur;
+  }
+  if (best.base != -1 && best.len < 2)
+    best.base = -1;
+
+  /*
+   * Format the result.
+   */
+  tp = tmp;
+  for (i = 0; i < (int) ARRAY_SIZE(words); i++) {
+    /* Are we inside the best run of 0x00's? */
+    if (best.base != -1 && i >= best.base &&
+        i < (best.base + best.len)) {
+      if (i == best.base)
+        *tp++ = ':';
+      continue;
+    }
+    /* Are we following an initial run of 0x00s or any real hex? */
+    if (i != 0)
+      *tp++ = ':';
+    /* Is this address an encapsulated IPv4? */
+    if (i == 6 && best.base == 0 && (best.len == 6 ||
+        (best.len == 7 && words[7] != 0x0001) ||
+        (best.len == 5 && words[5] == 0xffff))) {
+      int err = inet_ntop4(src+12, tp, sizeof tmp - (tp - tmp));
+      if (err)
+        return err;
+      tp += strlen(tp);
+      break;
+    }
+    tp += sprintf(tp, "%x", words[i]);
+  }
+  /* Was it a trailing run of 0x00's? */
+  if (best.base != -1 && (best.base + best.len) == ARRAY_SIZE(words))
+    *tp++ = ':';
+  *tp++ = '\0';
+  if ((size_t) (tp - tmp) > size)
+    return UV_ENOSPC;
+  uv__strscpy(dst, tmp, size);
+  return 0;
+}
+
+
+int uv_inet_pton(int af, const char* src, void* dst) {
+  if (src == NULL || dst == NULL)
+    return UV_EINVAL;
+
+  switch (af) {
+  case AF_INET:
+    return (inet_pton4(src, (unsigned char*)dst));
+  case AF_INET6: {
+    int len;
+    char tmp[UV__INET6_ADDRSTRLEN], *s;
+    const char *p;
+    s = (char*) src;
+    p = strchr(src, '%');
+    if (p != NULL) {
+      s = tmp;
+      len = p - src;
+      if (len > UV__INET6_ADDRSTRLEN-1)
+        return UV_EINVAL;
+      memcpy(s, src, len);
+      s[len] = '\0';
+    }
+    return inet_pton6(s, (unsigned char*)dst);
+  }
+  default:
+    return UV_EAFNOSUPPORT;
+  }
+  /* NOTREACHED */
+}
+
+
+static int inet_pton4(const char *src, unsigned char *dst) {
+  static const char digits[] = "0123456789";
+  int saw_digit, octets, ch;
+  unsigned char tmp[sizeof(struct in_addr)], *tp;
+
+  saw_digit = 0;
+  octets = 0;
+  *(tp = tmp) = 0;
+  while ((ch = *src++) != '\0') {
+    const char *pch;
+
+    if ((pch = strchr(digits, ch)) != NULL) {
+      unsigned int nw = *tp * 10 + (pch - digits);
+
+      if (saw_digit && *tp == 0)
+        return UV_EINVAL;
+      if (nw > 255)
+        return UV_EINVAL;
+      *tp = nw;
+      if (!saw_digit) {
+        if (++octets > 4)
+          return UV_EINVAL;
+        saw_digit = 1;
+      }
+    } else if (ch == '.' && saw_digit) {
+      if (octets == 4)
+        return UV_EINVAL;
+      *++tp = 0;
+      saw_digit = 0;
+    } else
+      return UV_EINVAL;
+  }
+  if (octets < 4)
+    return UV_EINVAL;
+  memcpy(dst, tmp, sizeof(struct in_addr));
+  return 0;
+}
+
+
+static int inet_pton6(const char *src, unsigned char *dst) {
+  static const char xdigits_l[] = "0123456789abcdef",
+                    xdigits_u[] = "0123456789ABCDEF";
+  unsigned char tmp[sizeof(struct in6_addr)], *tp, *endp, *colonp;
+  const char *xdigits, *curtok;
+  int ch, seen_xdigits;
+  unsigned int val;
+
+  memset((tp = tmp), '\0', sizeof tmp);
+  endp = tp + sizeof tmp;
+  colonp = NULL;
+  /* Leading :: requires some special handling. */
+  if (*src == ':')
+    if (*++src != ':')
+      return UV_EINVAL;
+  curtok = src;
+  seen_xdigits = 0;
+  val = 0;
+  while ((ch = *src++) != '\0') {
+    const char *pch;
+
+    if ((pch = strchr((xdigits = xdigits_l), ch)) == NULL)
+      pch = strchr((xdigits = xdigits_u), ch);
+    if (pch != NULL) {
+      val <<= 4;
+      val |= (pch - xdigits);
+      if (++seen_xdigits > 4)
+        return UV_EINVAL;
+      continue;
+    }
+    if (ch == ':') {
+      curtok = src;
+      if (!seen_xdigits) {
+        if (colonp)
+          return UV_EINVAL;
+        colonp = tp;
+        continue;
+      } else if (*src == '\0') {
+        return UV_EINVAL;
+      }
+      if (tp + sizeof(uint16_t) > endp)
+        return UV_EINVAL;
+      *tp++ = (unsigned char) (val >> 8) & 0xff;
+      *tp++ = (unsigned char) val & 0xff;
+      seen_xdigits = 0;
+      val = 0;
+      continue;
+    }
+    if (ch == '.' && ((tp + sizeof(struct in_addr)) <= endp)) {
+      int err = inet_pton4(curtok, tp);
+      if (err == 0) {
+        tp += sizeof(struct in_addr);
+        seen_xdigits = 0;
+        break;  /*%< '\\0' was seen by inet_pton4(). */
+      }
+    }
+    return UV_EINVAL;
+  }
+  if (seen_xdigits) {
+    if (tp + sizeof(uint16_t) > endp)
+      return UV_EINVAL;
+    *tp++ = (unsigned char) (val >> 8) & 0xff;
+    *tp++ = (unsigned char) val & 0xff;
+  }
+  if (colonp != NULL) {
+    /*
+     * Since some memmove()'s erroneously fail to handle
+     * overlapping regions, we'll do the shift by hand.
+     */
+    const int n = tp - colonp;
+    int i;
+
+    if (tp == endp)
+      return UV_EINVAL;
+    for (i = 1; i <= n; i++) {
+      endp[- i] = colonp[n - i];
+      colonp[n - i] = 0;
+    }
+    tp = endp;
+  }
+  if (tp != endp)
+    return UV_EINVAL;
+  memcpy(dst, tmp, sizeof tmp);
+  return 0;
+}
diff --git a/wpinet/src/main/native/thirdparty/libuv/src/queue.h b/wpinet/src/main/native/thirdparty/libuv/src/queue.h
new file mode 100644
index 0000000..ff3540a
--- /dev/null
+++ b/wpinet/src/main/native/thirdparty/libuv/src/queue.h
@@ -0,0 +1,108 @@
+/* Copyright (c) 2013, Ben Noordhuis <info@bnoordhuis.nl>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef QUEUE_H_
+#define QUEUE_H_
+
+#include <stddef.h>
+
+typedef void *QUEUE[2];
+
+/* Private macros. */
+#define QUEUE_NEXT(q)       (*(QUEUE **) &((*(q))[0]))
+#define QUEUE_PREV(q)       (*(QUEUE **) &((*(q))[1]))
+#define QUEUE_PREV_NEXT(q)  (QUEUE_NEXT(QUEUE_PREV(q)))
+#define QUEUE_NEXT_PREV(q)  (QUEUE_PREV(QUEUE_NEXT(q)))
+
+/* Public macros. */
+#define QUEUE_DATA(ptr, type, field)                                          \
+  ((type *) ((char *) (ptr) - offsetof(type, field)))
+
+/* Important note: mutating the list while QUEUE_FOREACH is
+ * iterating over its elements results in undefined behavior.
+ */
+#define QUEUE_FOREACH(q, h)                                                   \
+  for ((q) = QUEUE_NEXT(h); (q) != (h); (q) = QUEUE_NEXT(q))
+
+#define QUEUE_EMPTY(q)                                                        \
+  ((const QUEUE *) (q) == (const QUEUE *) QUEUE_NEXT(q))
+
+#define QUEUE_HEAD(q)                                                         \
+  (QUEUE_NEXT(q))
+
+#define QUEUE_INIT(q)                                                         \
+  do {                                                                        \
+    QUEUE_NEXT(q) = (q);                                                      \
+    QUEUE_PREV(q) = (q);                                                      \
+  }                                                                           \
+  while (0)
+
+#define QUEUE_ADD(h, n)                                                       \
+  do {                                                                        \
+    QUEUE_PREV_NEXT(h) = QUEUE_NEXT(n);                                       \
+    QUEUE_NEXT_PREV(n) = QUEUE_PREV(h);                                       \
+    QUEUE_PREV(h) = QUEUE_PREV(n);                                            \
+    QUEUE_PREV_NEXT(h) = (h);                                                 \
+  }                                                                           \
+  while (0)
+
+#define QUEUE_SPLIT(h, q, n)                                                  \
+  do {                                                                        \
+    QUEUE_PREV(n) = QUEUE_PREV(h);                                            \
+    QUEUE_PREV_NEXT(n) = (n);                                                 \
+    QUEUE_NEXT(n) = (q);                                                      \
+    QUEUE_PREV(h) = QUEUE_PREV(q);                                            \
+    QUEUE_PREV_NEXT(h) = (h);                                                 \
+    QUEUE_PREV(q) = (n);                                                      \
+  }                                                                           \
+  while (0)
+
+#define QUEUE_MOVE(h, n)                                                      \
+  do {                                                                        \
+    if (QUEUE_EMPTY(h))                                                       \
+      QUEUE_INIT(n);                                                          \
+    else {                                                                    \
+      QUEUE* q = QUEUE_HEAD(h);                                               \
+      QUEUE_SPLIT(h, q, n);                                                   \
+    }                                                                         \
+  }                                                                           \
+  while (0)
+
+#define QUEUE_INSERT_HEAD(h, q)                                               \
+  do {                                                                        \
+    QUEUE_NEXT(q) = QUEUE_NEXT(h);                                            \
+    QUEUE_PREV(q) = (h);                                                      \
+    QUEUE_NEXT_PREV(q) = (q);                                                 \
+    QUEUE_NEXT(h) = (q);                                                      \
+  }                                                                           \
+  while (0)
+
+#define QUEUE_INSERT_TAIL(h, q)                                               \
+  do {                                                                        \
+    QUEUE_NEXT(q) = (h);                                                      \
+    QUEUE_PREV(q) = QUEUE_PREV(h);                                            \
+    QUEUE_PREV_NEXT(q) = (q);                                                 \
+    QUEUE_PREV(h) = (q);                                                      \
+  }                                                                           \
+  while (0)
+
+#define QUEUE_REMOVE(q)                                                       \
+  do {                                                                        \
+    QUEUE_PREV_NEXT(q) = QUEUE_NEXT(q);                                       \
+    QUEUE_NEXT_PREV(q) = QUEUE_PREV(q);                                       \
+  }                                                                           \
+  while (0)
+
+#endif /* QUEUE_H_ */
diff --git a/wpinet/src/main/native/thirdparty/libuv/src/random.cpp b/wpinet/src/main/native/thirdparty/libuv/src/random.cpp
new file mode 100644
index 0000000..e75f77d
--- /dev/null
+++ b/wpinet/src/main/native/thirdparty/libuv/src/random.cpp
@@ -0,0 +1,123 @@
+/* Copyright libuv contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include "uv.h"
+#include "uv-common.h"
+
+#ifdef _WIN32
+#  include "win/internal.h"
+#else
+#  include "unix/internal.h"
+#endif
+
+static int uv__random(void* buf, size_t buflen) {
+  int rc;
+
+#if defined(__PASE__)
+  rc = uv__random_readpath("/dev/urandom", buf, buflen);
+#elif defined(_AIX) || defined(__QNX__)
+  rc = uv__random_readpath("/dev/random", buf, buflen);
+#elif defined(__APPLE__) || defined(__OpenBSD__) || \
+     (defined(__ANDROID_API__) && __ANDROID_API__ >= 28)
+  rc = uv__random_getentropy(buf, buflen);
+  if (rc == UV_ENOSYS)
+    rc = uv__random_devurandom(buf, buflen);
+#elif defined(__NetBSD__)
+  rc = uv__random_sysctl(buf, buflen);
+#elif defined(__FreeBSD__) || defined(__linux__)
+  rc = uv__random_getrandom(buf, buflen);
+  if (rc == UV_ENOSYS)
+    rc = uv__random_devurandom(buf, buflen);
+# if defined(__linux__)
+  switch (rc) {
+    case UV_EACCES:
+    case UV_EIO:
+    case UV_ELOOP:
+    case UV_EMFILE:
+    case UV_ENFILE:
+    case UV_ENOENT:
+    case UV_EPERM:
+      rc = uv__random_sysctl(buf, buflen);
+      break;
+  }
+# endif
+#elif defined(_WIN32)
+  uv__once_init();
+  rc = uv__random_rtlgenrandom(buf, buflen);
+#else
+  rc = uv__random_devurandom(buf, buflen);
+#endif
+
+  return rc;
+}
+
+
+static void uv__random_work(struct uv__work* w) {
+  uv_random_t* req;
+
+  req = container_of(w, uv_random_t, work_req);
+  req->status = uv__random(req->buf, req->buflen);
+}
+
+
+static void uv__random_done(struct uv__work* w, int status) {
+  uv_random_t* req;
+
+  req = container_of(w, uv_random_t, work_req);
+  uv__req_unregister(req->loop, req);
+
+  if (status == 0)
+    status = req->status;
+
+  req->cb(req, status, req->buf, req->buflen);
+}
+
+
+int uv_random(uv_loop_t* loop,
+              uv_random_t* req,
+              void *buf,
+              size_t buflen,
+              unsigned flags,
+              uv_random_cb cb) {
+  if (buflen > 0x7FFFFFFFu)
+    return UV_E2BIG;
+
+  if (flags != 0)
+    return UV_EINVAL;
+
+  if (cb == NULL)
+    return uv__random(buf, buflen);
+
+  uv__req_init(loop, req, UV_RANDOM);
+  req->loop = loop;
+  req->status = 0;
+  req->cb = cb;
+  req->buf = buf;
+  req->buflen = buflen;
+
+  uv__work_submit(loop,
+                  &req->work_req,
+                  UV__WORK_CPU,
+                  uv__random_work,
+                  uv__random_done);
+
+  return 0;
+}
diff --git a/wpinet/src/main/native/thirdparty/libuv/src/strscpy.cpp b/wpinet/src/main/native/thirdparty/libuv/src/strscpy.cpp
new file mode 100644
index 0000000..6b4cc3b
--- /dev/null
+++ b/wpinet/src/main/native/thirdparty/libuv/src/strscpy.cpp
@@ -0,0 +1,38 @@
+/* Copyright libuv project contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include "strscpy.h"
+#include <limits.h>  /* SSIZE_MAX */
+
+ssize_t uv__strscpy(char* d, const char* s, size_t n) {
+  size_t i;
+
+  for (i = 0; i < n; i++)
+    if ('\0' == (d[i] = s[i]))
+      return i > SSIZE_MAX ? (ssize_t) UV_E2BIG : (ssize_t) i;
+
+  if (i == 0)
+    return 0;
+
+  d[--i] = '\0';
+
+  return UV_E2BIG;
+}
diff --git a/wpinet/src/main/native/thirdparty/libuv/src/strscpy.h b/wpinet/src/main/native/thirdparty/libuv/src/strscpy.h
new file mode 100644
index 0000000..e8d4724
--- /dev/null
+++ b/wpinet/src/main/native/thirdparty/libuv/src/strscpy.h
@@ -0,0 +1,39 @@
+/* Copyright libuv project contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#ifndef UV_STRSCPY_H_
+#define UV_STRSCPY_H_
+
+/* Include uv.h for its definitions of size_t and ssize_t.
+ * size_t can be obtained directly from <stddef.h> but ssize_t requires
+ * some hoop jumping on Windows that I didn't want to duplicate here.
+ */
+#include "uv.h"
+
+/* Copies up to |n-1| bytes from |s| to |d| and always zero-terminates
+ * the result, except when |n==0|. Returns the number of bytes copied
+ * or UV_E2BIG if |d| is too small.
+ *
+ * See https://www.kernel.org/doc/htmldocs/kernel-api/API-strscpy.html
+ */
+ssize_t uv__strscpy(char* d, const char* s, size_t n);
+
+#endif  /* UV_STRSCPY_H_ */
diff --git a/wpinet/src/main/native/thirdparty/libuv/src/strtok.cpp b/wpinet/src/main/native/thirdparty/libuv/src/strtok.cpp
new file mode 100644
index 0000000..45ddea5
--- /dev/null
+++ b/wpinet/src/main/native/thirdparty/libuv/src/strtok.cpp
@@ -0,0 +1,52 @@
+/* Copyright libuv project contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include <stdlib.h>
+#include "strtok.h"
+
+char* uv__strtok(char* str, const char* sep, char** itr) {
+  const char* sep_itr;
+  char* tmp;
+  char* start;
+
+  if (str == NULL)
+    start = tmp = *itr;
+  else
+    start = tmp = str;
+
+  if (tmp == NULL)
+    return NULL;
+
+  while (*tmp != '\0') {
+    sep_itr = sep;
+    while (*sep_itr != '\0') {
+      if (*tmp == *sep_itr) {
+        *itr = tmp + 1;
+        *tmp = '\0';
+        return start;
+      }
+      sep_itr++;
+    }
+    tmp++;
+  }
+  *itr = NULL;
+  return start;
+}
diff --git a/wpinet/src/main/native/thirdparty/libuv/src/strtok.h b/wpinet/src/main/native/thirdparty/libuv/src/strtok.h
new file mode 100644
index 0000000..3799ff5
--- /dev/null
+++ b/wpinet/src/main/native/thirdparty/libuv/src/strtok.h
@@ -0,0 +1,27 @@
+/* Copyright libuv project contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#ifndef UV_STRTOK_H_
+#define UV_STRTOK_H_
+
+char* uv__strtok(char* str, const char* sep, char** itr);
+
+#endif  /* UV_STRTOK_H_ */
diff --git a/wpinet/src/main/native/thirdparty/libuv/src/threadpool.cpp b/wpinet/src/main/native/thirdparty/libuv/src/threadpool.cpp
new file mode 100644
index 0000000..718972c
--- /dev/null
+++ b/wpinet/src/main/native/thirdparty/libuv/src/threadpool.cpp
@@ -0,0 +1,397 @@
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include "uv-common.h"
+
+#if !defined(_WIN32)
+# include "unix/internal.h"
+#endif
+
+#include <stdlib.h>
+
+#ifdef _WIN32
+#pragma warning(disable: 6001 6011)
+#endif
+
+#define MAX_THREADPOOL_SIZE 1024
+
+static uv_once_t once = UV_ONCE_INIT;
+static uv_cond_t cond;
+static uv_mutex_t mutex;
+static unsigned int idle_threads;
+static unsigned int slow_io_work_running;
+static unsigned int nthreads;
+static uv_thread_t* threads;
+static uv_thread_t default_threads[4];
+static QUEUE exit_message;
+static QUEUE wq;
+static QUEUE run_slow_work_message;
+static QUEUE slow_io_pending_wq;
+
+static unsigned int slow_work_thread_threshold(void) {
+  return (nthreads + 1) / 2;
+}
+
+static void uv__cancelled(struct uv__work* w) {
+  abort();
+}
+
+
+/* To avoid deadlock with uv_cancel() it's crucial that the worker
+ * never holds the global mutex and the loop-local mutex at the same time.
+ */
+static void worker(void* arg) {
+  struct uv__work* w;
+  QUEUE* q;
+  int is_slow_work;
+
+  uv_sem_post((uv_sem_t*) arg);
+  arg = NULL;
+
+  uv_mutex_lock(&mutex);
+  for (;;) {
+    /* `mutex` should always be locked at this point. */
+
+    /* Keep waiting while either no work is present or only slow I/O
+       and we're at the threshold for that. */
+    while (QUEUE_EMPTY(&wq) ||
+           (QUEUE_HEAD(&wq) == &run_slow_work_message &&
+            QUEUE_NEXT(&run_slow_work_message) == &wq &&
+            slow_io_work_running >= slow_work_thread_threshold())) {
+      idle_threads += 1;
+      uv_cond_wait(&cond, &mutex);
+      idle_threads -= 1;
+    }
+
+    q = QUEUE_HEAD(&wq);
+    if (q == &exit_message) {
+      uv_cond_signal(&cond);
+      uv_mutex_unlock(&mutex);
+      break;
+    }
+
+    QUEUE_REMOVE(q);
+    QUEUE_INIT(q);  /* Signal uv_cancel() that the work req is executing. */
+
+    is_slow_work = 0;
+    if (q == &run_slow_work_message) {
+      /* If we're at the slow I/O threshold, re-schedule until after all
+         other work in the queue is done. */
+      if (slow_io_work_running >= slow_work_thread_threshold()) {
+        QUEUE_INSERT_TAIL(&wq, q);
+        continue;
+      }
+
+      /* If we encountered a request to run slow I/O work but there is none
+         to run, that means it's cancelled => Start over. */
+      if (QUEUE_EMPTY(&slow_io_pending_wq))
+        continue;
+
+      is_slow_work = 1;
+      slow_io_work_running++;
+
+      q = QUEUE_HEAD(&slow_io_pending_wq);
+      QUEUE_REMOVE(q);
+      QUEUE_INIT(q);
+
+      /* If there is more slow I/O work, schedule it to be run as well. */
+      if (!QUEUE_EMPTY(&slow_io_pending_wq)) {
+        QUEUE_INSERT_TAIL(&wq, &run_slow_work_message);
+        if (idle_threads > 0)
+          uv_cond_signal(&cond);
+      }
+    }
+
+    uv_mutex_unlock(&mutex);
+
+    w = QUEUE_DATA(q, struct uv__work, wq);
+    w->work(w);
+
+    uv_mutex_lock(&w->loop->wq_mutex);
+    w->work = NULL;  /* Signal uv_cancel() that the work req is done
+                        executing. */
+    QUEUE_INSERT_TAIL(&w->loop->wq, &w->wq);
+    uv_async_send(&w->loop->wq_async);
+    uv_mutex_unlock(&w->loop->wq_mutex);
+
+    /* Lock `mutex` since that is expected at the start of the next
+     * iteration. */
+    uv_mutex_lock(&mutex);
+    if (is_slow_work) {
+      /* `slow_io_work_running` is protected by `mutex`. */
+      slow_io_work_running--;
+    }
+  }
+}
+
+
+static void post(QUEUE* q, enum uv__work_kind kind) {
+  uv_mutex_lock(&mutex);
+  if (kind == UV__WORK_SLOW_IO) {
+    /* Insert into a separate queue. */
+    QUEUE_INSERT_TAIL(&slow_io_pending_wq, q);
+    if (!QUEUE_EMPTY(&run_slow_work_message)) {
+      /* Running slow I/O tasks is already scheduled => Nothing to do here.
+         The worker that runs said other task will schedule this one as well. */
+      uv_mutex_unlock(&mutex);
+      return;
+    }
+    q = &run_slow_work_message;
+  }
+
+  QUEUE_INSERT_TAIL(&wq, q);
+  if (idle_threads > 0)
+    uv_cond_signal(&cond);
+  uv_mutex_unlock(&mutex);
+}
+
+
+#ifdef __MVS__
+/* TODO(itodorov) - zos: revisit when Woz compiler is available. */
+__attribute__((destructor))
+#endif
+void uv__threadpool_cleanup(void) {
+  unsigned int i;
+
+  if (nthreads == 0)
+    return;
+
+#ifndef __MVS__
+  /* TODO(gabylb) - zos: revisit when Woz compiler is available. */
+  post(&exit_message, UV__WORK_CPU);
+#endif
+
+  for (i = 0; i < nthreads; i++)
+    if (uv_thread_join(threads + i))
+      abort();
+
+  if (threads != default_threads)
+    uv__free(threads);
+
+  uv_mutex_destroy(&mutex);
+  uv_cond_destroy(&cond);
+
+  threads = NULL;
+  nthreads = 0;
+}
+
+
+static void init_threads(void) {
+  unsigned int i;
+  const char* val;
+  uv_sem_t sem;
+
+  nthreads = ARRAY_SIZE(default_threads);
+  val = getenv("UV_THREADPOOL_SIZE");
+  if (val != NULL)
+    nthreads = atoi(val);
+  if (nthreads == 0)
+    nthreads = 1;
+  if (nthreads > MAX_THREADPOOL_SIZE)
+    nthreads = MAX_THREADPOOL_SIZE;
+
+  threads = default_threads;
+  if (nthreads > ARRAY_SIZE(default_threads)) {
+    threads = (uv_thread_t*)uv__malloc(nthreads * sizeof(threads[0]));
+    if (threads == NULL) {
+      nthreads = ARRAY_SIZE(default_threads);
+      threads = default_threads;
+    }
+  }
+
+  if (uv_cond_init(&cond))
+    abort();
+
+  if (uv_mutex_init(&mutex))
+    abort();
+
+  QUEUE_INIT(&wq);
+  QUEUE_INIT(&slow_io_pending_wq);
+  QUEUE_INIT(&run_slow_work_message);
+
+  if (uv_sem_init(&sem, 0))
+    abort();
+
+  for (i = 0; i < nthreads; i++)
+    if (uv_thread_create(threads + i, worker, &sem))
+      abort();
+
+  for (i = 0; i < nthreads; i++)
+    uv_sem_wait(&sem);
+
+  uv_sem_destroy(&sem);
+}
+
+
+#ifndef _WIN32
+static void reset_once(void) {
+  uv_once_t child_once = UV_ONCE_INIT;
+  memcpy(&once, &child_once, sizeof(child_once));
+}
+#endif
+
+
+static void init_once(void) {
+#ifndef _WIN32
+  /* Re-initialize the threadpool after fork.
+   * Note that this discards the global mutex and condition as well
+   * as the work queue.
+   */
+  if (pthread_atfork(NULL, NULL, &reset_once))
+    abort();
+#endif
+  init_threads();
+}
+
+
+void uv__work_submit(uv_loop_t* loop,
+                     struct uv__work* w,
+                     enum uv__work_kind kind,
+                     void (*work)(struct uv__work* w),
+                     void (*done)(struct uv__work* w, int status)) {
+  uv_once(&once, init_once);
+  w->loop = loop;
+  w->work = work;
+  w->done = done;
+  post(&w->wq, kind);
+}
+
+
+static int uv__work_cancel(uv_loop_t* loop, uv_req_t* req, struct uv__work* w) {
+  int cancelled;
+
+  uv_mutex_lock(&mutex);
+  uv_mutex_lock(&w->loop->wq_mutex);
+
+  cancelled = !QUEUE_EMPTY(&w->wq) && w->work != NULL;
+  if (cancelled)
+    QUEUE_REMOVE(&w->wq);
+
+  uv_mutex_unlock(&w->loop->wq_mutex);
+  uv_mutex_unlock(&mutex);
+
+  if (!cancelled)
+    return UV_EBUSY;
+
+  w->work = uv__cancelled;
+  uv_mutex_lock(&loop->wq_mutex);
+  QUEUE_INSERT_TAIL(&loop->wq, &w->wq);
+  uv_async_send(&loop->wq_async);
+  uv_mutex_unlock(&loop->wq_mutex);
+
+  return 0;
+}
+
+
+void uv__work_done(uv_async_t* handle) {
+  struct uv__work* w;
+  uv_loop_t* loop;
+  QUEUE* q;
+  QUEUE wq;
+  int err;
+
+  loop = container_of(handle, uv_loop_t, wq_async);
+  uv_mutex_lock(&loop->wq_mutex);
+  QUEUE_MOVE(&loop->wq, &wq);
+  uv_mutex_unlock(&loop->wq_mutex);
+
+  while (!QUEUE_EMPTY(&wq)) {
+    q = QUEUE_HEAD(&wq);
+    QUEUE_REMOVE(q);
+
+    w = container_of(q, struct uv__work, wq);
+    err = (w->work == uv__cancelled) ? UV_ECANCELED : 0;
+    w->done(w, err);
+  }
+}
+
+
+static void uv__queue_work(struct uv__work* w) {
+  uv_work_t* req = container_of(w, uv_work_t, work_req);
+
+  req->work_cb(req);
+}
+
+
+static void uv__queue_done(struct uv__work* w, int err) {
+  uv_work_t* req;
+
+  req = container_of(w, uv_work_t, work_req);
+  uv__req_unregister(req->loop, req);
+
+  if (req->after_work_cb == NULL)
+    return;
+
+  req->after_work_cb(req, err);
+}
+
+
+int uv_queue_work(uv_loop_t* loop,
+                  uv_work_t* req,
+                  uv_work_cb work_cb,
+                  uv_after_work_cb after_work_cb) {
+  if (work_cb == NULL)
+    return UV_EINVAL;
+
+  uv__req_init(loop, req, UV_WORK);
+  req->loop = loop;
+  req->work_cb = work_cb;
+  req->after_work_cb = after_work_cb;
+  uv__work_submit(loop,
+                  &req->work_req,
+                  UV__WORK_CPU,
+                  uv__queue_work,
+                  uv__queue_done);
+  return 0;
+}
+
+
+int uv_cancel(uv_req_t* req) {
+  struct uv__work* wreq;
+  uv_loop_t* loop;
+
+  switch (req->type) {
+  case UV_FS:
+    loop =  ((uv_fs_t*) req)->loop;
+    wreq = &((uv_fs_t*) req)->work_req;
+    break;
+  case UV_GETADDRINFO:
+    loop =  ((uv_getaddrinfo_t*) req)->loop;
+    wreq = &((uv_getaddrinfo_t*) req)->work_req;
+    break;
+  case UV_GETNAMEINFO:
+    loop = ((uv_getnameinfo_t*) req)->loop;
+    wreq = &((uv_getnameinfo_t*) req)->work_req;
+    break;
+  case UV_RANDOM:
+    loop = ((uv_random_t*) req)->loop;
+    wreq = &((uv_random_t*) req)->work_req;
+    break;
+  case UV_WORK:
+    loop =  ((uv_work_t*) req)->loop;
+    wreq = &((uv_work_t*) req)->work_req;
+    break;
+  default:
+    return UV_EINVAL;
+  }
+
+  return uv__work_cancel(loop, req, wreq);
+}
diff --git a/wpinet/src/main/native/thirdparty/libuv/src/timer.cpp b/wpinet/src/main/native/thirdparty/libuv/src/timer.cpp
new file mode 100644
index 0000000..bc680e7
--- /dev/null
+++ b/wpinet/src/main/native/thirdparty/libuv/src/timer.cpp
@@ -0,0 +1,185 @@
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include "uv.h"
+#include "uv-common.h"
+#include "heap-inl.h"
+
+#include <assert.h>
+#include <limits.h>
+
+
+static struct heap *timer_heap(const uv_loop_t* loop) {
+#ifdef _WIN32
+  return (struct heap*) loop->timer_heap;
+#else
+  return (struct heap*) &loop->timer_heap;
+#endif
+}
+
+
+static int timer_less_than(const struct heap_node* ha,
+                           const struct heap_node* hb) {
+  const uv_timer_t* a;
+  const uv_timer_t* b;
+
+  a = container_of(ha, uv_timer_t, heap_node);
+  b = container_of(hb, uv_timer_t, heap_node);
+
+  if (a->timeout < b->timeout)
+    return 1;
+  if (b->timeout < a->timeout)
+    return 0;
+
+  /* Compare start_id when both have the same timeout. start_id is
+   * allocated with loop->timer_counter in uv_timer_start().
+   */
+  return a->start_id < b->start_id;
+}
+
+
+int uv_timer_init(uv_loop_t* loop, uv_timer_t* handle) {
+  uv__handle_init(loop, (uv_handle_t*)handle, UV_TIMER);
+  handle->timer_cb = NULL;
+  handle->timeout = 0;
+  handle->repeat = 0;
+  return 0;
+}
+
+
+int uv_timer_start(uv_timer_t* handle,
+                   uv_timer_cb cb,
+                   uint64_t timeout,
+                   uint64_t repeat) {
+  uint64_t clamped_timeout;
+
+  if (uv__is_closing(handle) || cb == NULL)
+    return UV_EINVAL;
+
+  if (uv__is_active(handle))
+    uv_timer_stop(handle);
+
+  clamped_timeout = handle->loop->time + timeout;
+  if (clamped_timeout < timeout)
+    clamped_timeout = (uint64_t) -1;
+
+  handle->timer_cb = cb;
+  handle->timeout = clamped_timeout;
+  handle->repeat = repeat;
+  /* start_id is the second index to be compared in timer_less_than() */
+  handle->start_id = handle->loop->timer_counter++;
+
+  heap_insert(timer_heap(handle->loop),
+              (struct heap_node*) &handle->heap_node,
+              timer_less_than);
+  uv__handle_start(handle);
+
+  return 0;
+}
+
+
+int uv_timer_stop(uv_timer_t* handle) {
+  if (!uv__is_active(handle))
+    return 0;
+
+  heap_remove(timer_heap(handle->loop),
+              (struct heap_node*) &handle->heap_node,
+              timer_less_than);
+  uv__handle_stop(handle);
+
+  return 0;
+}
+
+
+int uv_timer_again(uv_timer_t* handle) {
+  if (handle->timer_cb == NULL)
+    return UV_EINVAL;
+
+  if (handle->repeat) {
+    uv_timer_stop(handle);
+    uv_timer_start(handle, handle->timer_cb, handle->repeat, handle->repeat);
+  }
+
+  return 0;
+}
+
+
+void uv_timer_set_repeat(uv_timer_t* handle, uint64_t repeat) {
+  handle->repeat = repeat;
+}
+
+
+uint64_t uv_timer_get_repeat(const uv_timer_t* handle) {
+  return handle->repeat;
+}
+
+
+uint64_t uv_timer_get_due_in(const uv_timer_t* handle) {
+  if (handle->loop->time >= handle->timeout)
+    return 0;
+
+  return handle->timeout - handle->loop->time;
+}
+
+
+int uv__next_timeout(const uv_loop_t* loop) {
+  const struct heap_node* heap_node;
+  const uv_timer_t* handle;
+  uint64_t diff;
+
+  heap_node = heap_min(timer_heap(loop));
+  if (heap_node == NULL)
+    return -1; /* block indefinitely */
+
+  handle = container_of(heap_node, uv_timer_t, heap_node);
+  if (handle->timeout <= loop->time)
+    return 0;
+
+  diff = handle->timeout - loop->time;
+  if (diff > INT_MAX)
+    diff = INT_MAX;
+
+  return (int) diff;
+}
+
+
+void uv__run_timers(uv_loop_t* loop) {
+  struct heap_node* heap_node;
+  uv_timer_t* handle;
+
+  for (;;) {
+    heap_node = heap_min(timer_heap(loop));
+    if (heap_node == NULL)
+      break;
+
+    handle = container_of(heap_node, uv_timer_t, heap_node);
+    if (handle->timeout > loop->time)
+      break;
+
+    uv_timer_stop(handle);
+    uv_timer_again(handle);
+    handle->timer_cb(handle);
+  }
+}
+
+
+void uv__timer_close(uv_timer_t* handle) {
+  uv_timer_stop(handle);
+}
diff --git a/wpinet/src/main/native/thirdparty/libuv/src/unix/async.cpp b/wpinet/src/main/native/thirdparty/libuv/src/unix/async.cpp
new file mode 100644
index 0000000..e1805c3
--- /dev/null
+++ b/wpinet/src/main/native/thirdparty/libuv/src/unix/async.cpp
@@ -0,0 +1,253 @@
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+/* This file contains both the uv__async internal infrastructure and the
+ * user-facing uv_async_t functions.
+ */
+
+#include "uv.h"
+#include "internal.h"
+#include "atomic-ops.h"
+
+#include <errno.h>
+#include <stdio.h>  /* snprintf() */
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sched.h>  /* sched_yield() */
+
+#ifdef __linux__
+#include <sys/eventfd.h>
+#endif
+
+static void uv__async_send(uv_loop_t* loop);
+static int uv__async_start(uv_loop_t* loop);
+
+
+int uv_async_init(uv_loop_t* loop, uv_async_t* handle, uv_async_cb async_cb) {
+  int err;
+
+  err = uv__async_start(loop);
+  if (err)
+    return err;
+
+  uv__handle_init(loop, (uv_handle_t*)handle, UV_ASYNC);
+  handle->async_cb = async_cb;
+  handle->pending = 0;
+
+  QUEUE_INSERT_TAIL(&loop->async_handles, &handle->queue);
+  uv__handle_start(handle);
+
+  return 0;
+}
+
+
+int uv_async_send(uv_async_t* handle) {
+  /* Do a cheap read first. */
+  if (ACCESS_ONCE(int, handle->pending) != 0)
+    return 0;
+
+  /* Tell the other thread we're busy with the handle. */
+  if (cmpxchgi(&handle->pending, 0, 1) != 0)
+    return 0;
+
+  /* Wake up the other thread's event loop. */
+  uv__async_send(handle->loop);
+
+  /* Tell the other thread we're done. */
+  if (cmpxchgi(&handle->pending, 1, 2) != 1)
+    abort();
+
+  return 0;
+}
+
+
+/* Only call this from the event loop thread. */
+static int uv__async_spin(uv_async_t* handle) {
+  int i;
+  int rc;
+
+  for (;;) {
+    /* 997 is not completely chosen at random. It's a prime number, acyclical
+     * by nature, and should therefore hopefully dampen sympathetic resonance.
+     */
+    for (i = 0; i < 997; i++) {
+      /* rc=0 -- handle is not pending.
+       * rc=1 -- handle is pending, other thread is still working with it.
+       * rc=2 -- handle is pending, other thread is done.
+       */
+      rc = cmpxchgi(&handle->pending, 2, 0);
+
+      if (rc != 1)
+        return rc;
+
+      /* Other thread is busy with this handle, spin until it's done. */
+      cpu_relax();
+    }
+
+    /* Yield the CPU. We may have preempted the other thread while it's
+     * inside the critical section and if it's running on the same CPU
+     * as us, we'll just burn CPU cycles until the end of our time slice.
+     */
+    sched_yield();
+  }
+}
+
+
+void uv__async_close(uv_async_t* handle) {
+  uv__async_spin(handle);
+  QUEUE_REMOVE(&handle->queue);
+  uv__handle_stop(handle);
+}
+
+
+static void uv__async_io(uv_loop_t* loop, uv__io_t* w, unsigned int events) {
+  char buf[1024];
+  ssize_t r;
+  QUEUE queue;
+  QUEUE* q;
+  uv_async_t* h;
+
+  assert(w == &loop->async_io_watcher);
+
+  for (;;) {
+    r = read(w->fd, buf, sizeof(buf));
+
+    if (r == sizeof(buf))
+      continue;
+
+    if (r != -1)
+      break;
+
+    if (errno == EAGAIN || errno == EWOULDBLOCK)
+      break;
+
+    if (errno == EINTR)
+      continue;
+
+    abort();
+  }
+
+  QUEUE_MOVE(&loop->async_handles, &queue);
+  while (!QUEUE_EMPTY(&queue)) {
+    q = QUEUE_HEAD(&queue);
+    h = QUEUE_DATA(q, uv_async_t, queue);
+
+    QUEUE_REMOVE(q);
+    QUEUE_INSERT_TAIL(&loop->async_handles, q);
+
+    if (0 == uv__async_spin(h))
+      continue;  /* Not pending. */
+
+    if (h->async_cb == NULL)
+      continue;
+
+    h->async_cb(h);
+  }
+}
+
+
+static void uv__async_send(uv_loop_t* loop) {
+  const void* buf;
+  ssize_t len;
+  int fd;
+  int r;
+
+  buf = "";
+  len = 1;
+  fd = loop->async_wfd;
+
+#if defined(__linux__)
+  if (fd == -1) {
+    static const uint64_t val = 1;
+    buf = &val;
+    len = sizeof(val);
+    fd = loop->async_io_watcher.fd;  /* eventfd */
+  }
+#endif
+
+  do
+    r = write(fd, buf, len);
+  while (r == -1 && errno == EINTR);
+
+  if (r == len)
+    return;
+
+  if (r == -1)
+    if (errno == EAGAIN || errno == EWOULDBLOCK)
+      return;
+
+  abort();
+}
+
+
+static int uv__async_start(uv_loop_t* loop) {
+  int pipefd[2];
+  int err;
+
+  if (loop->async_io_watcher.fd != -1)
+    return 0;
+
+#ifdef __linux__
+  err = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK);
+  if (err < 0)
+    return UV__ERR(errno);
+
+  pipefd[0] = err;
+  pipefd[1] = -1;
+#else
+  err = uv__make_pipe(pipefd, UV_NONBLOCK_PIPE);
+  if (err < 0)
+    return err;
+#endif
+
+  uv__io_init(&loop->async_io_watcher, uv__async_io, pipefd[0]);
+  uv__io_start(loop, &loop->async_io_watcher, POLLIN);
+  loop->async_wfd = pipefd[1];
+
+  return 0;
+}
+
+
+int uv__async_fork(uv_loop_t* loop) {
+  if (loop->async_io_watcher.fd == -1) /* never started */
+    return 0;
+
+  uv__async_stop(loop);
+
+  return uv__async_start(loop);
+}
+
+
+void uv__async_stop(uv_loop_t* loop) {
+  if (loop->async_io_watcher.fd == -1)
+    return;
+
+  if (loop->async_wfd != -1) {
+    if (loop->async_wfd != loop->async_io_watcher.fd)
+      uv__close(loop->async_wfd);
+    loop->async_wfd = -1;
+  }
+
+  uv__io_stop(loop, &loop->async_io_watcher, POLLIN);
+  uv__close(loop->async_io_watcher.fd);
+  loop->async_io_watcher.fd = -1;
+}
diff --git a/wpinet/src/main/native/thirdparty/libuv/src/unix/atomic-ops.h b/wpinet/src/main/native/thirdparty/libuv/src/unix/atomic-ops.h
new file mode 100644
index 0000000..58043c4
--- /dev/null
+++ b/wpinet/src/main/native/thirdparty/libuv/src/unix/atomic-ops.h
@@ -0,0 +1,64 @@
+/* Copyright (c) 2013, Ben Noordhuis <info@bnoordhuis.nl>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef UV_ATOMIC_OPS_H_
+#define UV_ATOMIC_OPS_H_
+
+#include "internal.h"  /* UV_UNUSED */
+
+#if defined(__SUNPRO_C) || defined(__SUNPRO_CC)
+#include <atomic.h>
+#endif
+
+UV_UNUSED(static int cmpxchgi(int* ptr, int oldval, int newval));
+UV_UNUSED(static void cpu_relax(void));
+
+/* Prefer hand-rolled assembly over the gcc builtins because the latter also
+ * issue full memory barriers.
+ */
+UV_UNUSED(static int cmpxchgi(int* ptr, int oldval, int newval)) {
+#if defined(__i386__) || defined(__x86_64__)
+  int out;
+  __asm__ __volatile__ ("lock; cmpxchg %2, %1;"
+                        : "=a" (out), "+m" (*(volatile int*) ptr)
+                        : "r" (newval), "0" (oldval)
+                        : "memory");
+  return out;
+#elif defined(__MVS__)
+  /* Use hand-rolled assembly because codegen from builtin __plo_CSST results in
+   * a runtime bug.
+   */
+  __asm(" cs %0,%2,%1 \n " : "+r"(oldval), "+m"(*ptr) : "r"(newval) :);
+  return oldval;
+#elif defined(__SUNPRO_C) || defined(__SUNPRO_CC)
+  return atomic_cas_uint((uint_t *)ptr, (uint_t)oldval, (uint_t)newval);
+#else
+  return __sync_val_compare_and_swap(ptr, oldval, newval);
+#endif
+}
+
+UV_UNUSED(static void cpu_relax(void)) {
+#if defined(__i386__) || defined(__x86_64__)
+  __asm__ __volatile__ ("rep; nop" ::: "memory");  /* a.k.a. PAUSE */
+#elif (defined(__arm__) && __ARM_ARCH >= 7) || defined(__aarch64__)
+  __asm__ __volatile__ ("yield" ::: "memory");
+#elif (defined(__ppc__) || defined(__ppc64__)) && defined(__APPLE__)
+  __asm volatile ("" : : : "memory");
+#elif !defined(__APPLE__) && (defined(__powerpc64__) || defined(__ppc64__) || defined(__PPC64__))
+  __asm__ __volatile__ ("or 1,1,1; or 2,2,2" ::: "memory");
+#endif
+}
+
+#endif  /* UV_ATOMIC_OPS_H_ */
diff --git a/wpinet/src/main/native/thirdparty/libuv/src/unix/bsd-ifaddrs.cpp b/wpinet/src/main/native/thirdparty/libuv/src/unix/bsd-ifaddrs.cpp
new file mode 100644
index 0000000..c3dd71a
--- /dev/null
+++ b/wpinet/src/main/native/thirdparty/libuv/src/unix/bsd-ifaddrs.cpp
@@ -0,0 +1,163 @@
+/* Copyright libuv project contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include "uv.h"
+#include "internal.h"
+
+#include <errno.h>
+#include <stddef.h>
+
+#include <ifaddrs.h>
+#include <net/if.h>
+#if !defined(__CYGWIN__) && !defined(__MSYS__) && !defined(__GNU__)
+#include <net/if_dl.h>
+#endif
+
+#if defined(__HAIKU__)
+#define IFF_RUNNING IFF_LINK
+#endif
+
+static int uv__ifaddr_exclude(struct ifaddrs *ent, int exclude_type) {
+  if (!((ent->ifa_flags & IFF_UP) && (ent->ifa_flags & IFF_RUNNING)))
+    return 1;
+  if (ent->ifa_addr == NULL)
+    return 1;
+#if !defined(__CYGWIN__) && !defined(__MSYS__) && !defined(__GNU__)
+  /*
+   * If `exclude_type` is `UV__EXCLUDE_IFPHYS`, return whether `sa_family`
+   * equals `AF_LINK`. Otherwise, the result depends on the operating
+   * system with `AF_LINK` or `PF_INET`.
+   */
+  if (exclude_type == UV__EXCLUDE_IFPHYS)
+    return (ent->ifa_addr->sa_family != AF_LINK);
+#endif
+#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__DragonFly__) || \
+    defined(__HAIKU__)
+  /*
+   * On BSD getifaddrs returns information related to the raw underlying
+   * devices. We're not interested in this information.
+   */
+  if (ent->ifa_addr->sa_family == AF_LINK)
+    return 1;
+#elif defined(__NetBSD__) || defined(__OpenBSD__)
+  if (ent->ifa_addr->sa_family != PF_INET &&
+      ent->ifa_addr->sa_family != PF_INET6)
+    return 1;
+#endif
+  return 0;
+}
+
+int uv_interface_addresses(uv_interface_address_t** addresses, int* count) {
+  struct ifaddrs* addrs;
+  struct ifaddrs* ent;
+  uv_interface_address_t* address;
+#if !(defined(__CYGWIN__) || defined(__MSYS__)) && !defined(__GNU__)
+  int i;
+#endif
+
+  *count = 0;
+  *addresses = NULL;
+
+  if (getifaddrs(&addrs) != 0)
+    return UV__ERR(errno);
+
+  /* Count the number of interfaces */
+  for (ent = addrs; ent != NULL; ent = ent->ifa_next) {
+    if (uv__ifaddr_exclude(ent, UV__EXCLUDE_IFADDR))
+      continue;
+    (*count)++;
+  }
+
+  if (*count == 0) {
+    freeifaddrs(addrs);
+    return 0;
+  }
+
+  /* Make sure the memory is initiallized to zero using calloc() */
+  *addresses = (uv_interface_address_t*)uv__calloc(*count, sizeof(**addresses));
+
+  if (*addresses == NULL) {
+    freeifaddrs(addrs);
+    return UV_ENOMEM;
+  }
+
+  address = *addresses;
+
+  for (ent = addrs; ent != NULL; ent = ent->ifa_next) {
+    if (uv__ifaddr_exclude(ent, UV__EXCLUDE_IFADDR))
+      continue;
+
+    address->name = uv__strdup(ent->ifa_name);
+
+    if (ent->ifa_addr->sa_family == AF_INET6) {
+      address->address.address6 = *((struct sockaddr_in6*) ent->ifa_addr);
+    } else {
+      address->address.address4 = *((struct sockaddr_in*) ent->ifa_addr);
+    }
+
+    if (ent->ifa_netmask == NULL) {
+      memset(&address->netmask, 0, sizeof(address->netmask));
+    } else if (ent->ifa_netmask->sa_family == AF_INET6) {
+      address->netmask.netmask6 = *((struct sockaddr_in6*) ent->ifa_netmask);
+    } else {
+      address->netmask.netmask4 = *((struct sockaddr_in*) ent->ifa_netmask);
+    }
+
+    address->is_internal = !!(ent->ifa_flags & IFF_LOOPBACK);
+
+    address++;
+  }
+
+#if !(defined(__CYGWIN__) || defined(__MSYS__)) && !defined(__GNU__)
+  /* Fill in physical addresses for each interface */
+  for (ent = addrs; ent != NULL; ent = ent->ifa_next) {
+    if (uv__ifaddr_exclude(ent, UV__EXCLUDE_IFPHYS))
+      continue;
+
+    address = *addresses;
+
+    for (i = 0; i < *count; i++) {
+      if (strcmp(address->name, ent->ifa_name) == 0) {
+        struct sockaddr_dl* sa_addr;
+        sa_addr = (struct sockaddr_dl*)(ent->ifa_addr);
+        memcpy(address->phys_addr, LLADDR(sa_addr), sizeof(address->phys_addr));
+      }
+      address++;
+    }
+  }
+#endif
+
+  freeifaddrs(addrs);
+
+  return 0;
+}
+
+
+void uv_free_interface_addresses(uv_interface_address_t* addresses,
+                                 int count) {
+  int i;
+
+  for (i = 0; i < count; i++) {
+    uv__free(addresses[i].name);
+  }
+
+  uv__free(addresses);
+}
diff --git a/wpinet/src/main/native/thirdparty/libuv/src/unix/core.cpp b/wpinet/src/main/native/thirdparty/libuv/src/unix/core.cpp
new file mode 100644
index 0000000..4c23f60
--- /dev/null
+++ b/wpinet/src/main/native/thirdparty/libuv/src/unix/core.cpp
@@ -0,0 +1,1676 @@
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include "uv.h"
+#include "internal.h"
+#include "strtok.h"
+
+#include <stddef.h> /* NULL */
+#include <stdio.h> /* printf */
+#include <stdlib.h>
+#include <string.h> /* strerror */
+#include <errno.h>
+#include <assert.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>  /* O_CLOEXEC */
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <limits.h> /* INT_MAX, PATH_MAX, IOV_MAX */
+#include <sys/uio.h> /* writev */
+#include <sys/resource.h> /* getrusage */
+#include <pwd.h>
+#include <sys/utsname.h>
+#include <sys/time.h>
+
+#ifdef __sun
+# include <sys/filio.h>
+# include <sys/types.h>
+# include <sys/wait.h>
+#endif
+
+#if defined(__APPLE__)
+# include <sys/filio.h>
+# endif /* defined(__APPLE__) */
+
+
+#if defined(__APPLE__) && !TARGET_OS_IPHONE
+# include <crt_externs.h>
+# include <mach-o/dyld.h> /* _NSGetExecutablePath */
+# define environ (*_NSGetEnviron())
+#else /* defined(__APPLE__) && !TARGET_OS_IPHONE */
+extern char** environ;
+#endif /* !(defined(__APPLE__) && !TARGET_OS_IPHONE) */
+
+
+#if defined(__DragonFly__)      || \
+    defined(__FreeBSD__)        || \
+    defined(__FreeBSD_kernel__) || \
+    defined(__NetBSD__)         || \
+    defined(__OpenBSD__)
+# include <sys/sysctl.h>
+# include <sys/filio.h>
+# include <sys/wait.h>
+# if defined(__FreeBSD__)
+#  define uv__accept4 accept4
+# endif
+# if defined(__NetBSD__)
+#  define uv__accept4(a, b, c, d) paccept((a), (b), (c), NULL, (d))
+# endif
+#endif
+
+#if defined(__MVS__)
+# include <sys/ioctl.h>
+# include "zos-sys-info.h"
+#endif
+
+#if defined(__linux__)
+# include <sched.h>
+# include <sys/syscall.h>
+# define uv__accept4 accept4
+#endif
+
+#if defined(__linux__) && defined(__SANITIZE_THREAD__) && defined(__clang__)
+# include <sanitizer/linux_syscall_hooks.h>
+#endif
+
+static void uv__run_pending(uv_loop_t* loop);
+
+/* Verify that uv_buf_t is ABI-compatible with struct iovec. */
+STATIC_ASSERT(sizeof(uv_buf_t) == sizeof(struct iovec));
+STATIC_ASSERT(sizeof(((uv_buf_t*) 0)->base) ==
+              sizeof(((struct iovec*) 0)->iov_base));
+STATIC_ASSERT(sizeof(((uv_buf_t*) 0)->len) ==
+              sizeof(((struct iovec*) 0)->iov_len));
+STATIC_ASSERT(offsetof(uv_buf_t, base) == offsetof(struct iovec, iov_base));
+STATIC_ASSERT(offsetof(uv_buf_t, len) == offsetof(struct iovec, iov_len));
+
+
+uint64_t uv_hrtime(void) {
+  return uv__hrtime(UV_CLOCK_PRECISE);
+}
+
+
+void uv_close(uv_handle_t* handle, uv_close_cb close_cb) {
+  assert(!uv__is_closing(handle));
+
+  handle->flags |= UV_HANDLE_CLOSING;
+  handle->close_cb = close_cb;
+
+  switch (handle->type) {
+  case UV_NAMED_PIPE:
+    uv__pipe_close((uv_pipe_t*)handle);
+    break;
+
+  case UV_TTY:
+    uv__stream_close((uv_stream_t*)handle);
+    break;
+
+  case UV_TCP:
+    uv__tcp_close((uv_tcp_t*)handle);
+    break;
+
+  case UV_UDP:
+    uv__udp_close((uv_udp_t*)handle);
+    break;
+
+  case UV_PREPARE:
+    uv__prepare_close((uv_prepare_t*)handle);
+    break;
+
+  case UV_CHECK:
+    uv__check_close((uv_check_t*)handle);
+    break;
+
+  case UV_IDLE:
+    uv__idle_close((uv_idle_t*)handle);
+    break;
+
+  case UV_ASYNC:
+    uv__async_close((uv_async_t*)handle);
+    break;
+
+  case UV_TIMER:
+    uv__timer_close((uv_timer_t*)handle);
+    break;
+
+  case UV_PROCESS:
+    uv__process_close((uv_process_t*)handle);
+    break;
+
+  case UV_FS_EVENT:
+    uv__fs_event_close((uv_fs_event_t*)handle);
+#if defined(__sun) || defined(__MVS__)
+    /*
+     * On Solaris, illumos, and z/OS we will not be able to dissociate the
+     * watcher for an event which is pending delivery, so we cannot always call
+     * uv__make_close_pending() straight away. The backend will call the
+     * function once the event has cleared.
+     */
+    return;
+#endif
+    break;
+
+  case UV_POLL:
+    uv__poll_close((uv_poll_t*)handle);
+    break;
+
+  case UV_FS_POLL:
+    uv__fs_poll_close((uv_fs_poll_t*)handle);
+    /* Poll handles use file system requests, and one of them may still be
+     * running. The poll code will call uv__make_close_pending() for us. */
+    return;
+
+  case UV_SIGNAL:
+    uv__signal_close((uv_signal_t*) handle);
+    break;
+
+  default:
+    assert(0);
+  }
+
+  uv__make_close_pending(handle);
+}
+
+int uv__socket_sockopt(uv_handle_t* handle, int optname, int* value) {
+  int r;
+  int fd;
+  socklen_t len;
+
+  if (handle == NULL || value == NULL)
+    return UV_EINVAL;
+
+  if (handle->type == UV_TCP || handle->type == UV_NAMED_PIPE)
+    fd = uv__stream_fd((uv_stream_t*) handle);
+  else if (handle->type == UV_UDP)
+    fd = ((uv_udp_t *) handle)->io_watcher.fd;
+  else
+    return UV_ENOTSUP;
+
+  len = sizeof(*value);
+
+  if (*value == 0)
+    r = getsockopt(fd, SOL_SOCKET, optname, value, &len);
+  else
+    r = setsockopt(fd, SOL_SOCKET, optname, (const void*) value, len);
+
+  if (r < 0)
+    return UV__ERR(errno);
+
+  return 0;
+}
+
+void uv__make_close_pending(uv_handle_t* handle) {
+  assert(handle->flags & UV_HANDLE_CLOSING);
+  assert(!(handle->flags & UV_HANDLE_CLOSED));
+  handle->next_closing = handle->loop->closing_handles;
+  handle->loop->closing_handles = handle;
+}
+
+int uv__getiovmax(void) {
+#if defined(IOV_MAX)
+  return IOV_MAX;
+#elif defined(_SC_IOV_MAX)
+  static int iovmax_cached = -1;
+  int iovmax;
+
+  iovmax = uv__load_relaxed(&iovmax_cached);
+  if (iovmax != -1)
+    return iovmax;
+
+  /* On some embedded devices (arm-linux-uclibc based ip camera),
+   * sysconf(_SC_IOV_MAX) can not get the correct value. The return
+   * value is -1 and the errno is EINPROGRESS. Degrade the value to 1.
+   */
+  iovmax = sysconf(_SC_IOV_MAX);
+  if (iovmax == -1)
+    iovmax = 1;
+
+  uv__store_relaxed(&iovmax_cached, iovmax);
+
+  return iovmax;
+#else
+  return 1024;
+#endif
+}
+
+
+static void uv__finish_close(uv_handle_t* handle) {
+  uv_signal_t* sh;
+
+  /* Note: while the handle is in the UV_HANDLE_CLOSING state now, it's still
+   * possible for it to be active in the sense that uv__is_active() returns
+   * true.
+   *
+   * A good example is when the user calls uv_shutdown(), immediately followed
+   * by uv_close(). The handle is considered active at this point because the
+   * completion of the shutdown req is still pending.
+   */
+  assert(handle->flags & UV_HANDLE_CLOSING);
+  assert(!(handle->flags & UV_HANDLE_CLOSED));
+  handle->flags |= UV_HANDLE_CLOSED;
+
+  switch (handle->type) {
+    case UV_PREPARE:
+    case UV_CHECK:
+    case UV_IDLE:
+    case UV_ASYNC:
+    case UV_TIMER:
+    case UV_PROCESS:
+    case UV_FS_EVENT:
+    case UV_FS_POLL:
+    case UV_POLL:
+      break;
+
+    case UV_SIGNAL:
+      /* If there are any caught signals "trapped" in the signal pipe,
+       * we can't call the close callback yet. Reinserting the handle
+       * into the closing queue makes the event loop spin but that's
+       * okay because we only need to deliver the pending events.
+       */
+      sh = (uv_signal_t*) handle;
+      if (sh->caught_signals > sh->dispatched_signals) {
+        handle->flags ^= UV_HANDLE_CLOSED;
+        uv__make_close_pending(handle);  /* Back into the queue. */
+        return;
+      }
+      break;
+
+    case UV_NAMED_PIPE:
+    case UV_TCP:
+    case UV_TTY:
+      uv__stream_destroy((uv_stream_t*)handle);
+      break;
+
+    case UV_UDP:
+      uv__udp_finish_close((uv_udp_t*)handle);
+      break;
+
+    default:
+      assert(0);
+      break;
+  }
+
+  uv__handle_unref(handle);
+  QUEUE_REMOVE(&handle->handle_queue);
+
+  if (handle->close_cb) {
+    handle->close_cb(handle);
+  }
+}
+
+
+static void uv__run_closing_handles(uv_loop_t* loop) {
+  uv_handle_t* p;
+  uv_handle_t* q;
+
+  p = loop->closing_handles;
+  loop->closing_handles = NULL;
+
+  while (p) {
+    q = p->next_closing;
+    uv__finish_close(p);
+    p = q;
+  }
+}
+
+
+int uv_is_closing(const uv_handle_t* handle) {
+  return uv__is_closing(handle);
+}
+
+
+int uv_backend_fd(const uv_loop_t* loop) {
+  return loop->backend_fd;
+}
+
+
+static int uv__loop_alive(const uv_loop_t* loop) {
+  return uv__has_active_handles(loop) ||
+         uv__has_active_reqs(loop) ||
+         !QUEUE_EMPTY(&loop->pending_queue) ||
+         loop->closing_handles != NULL;
+}
+
+
+static int uv__backend_timeout(const uv_loop_t* loop) {
+  if (loop->stop_flag == 0 &&
+      /* uv__loop_alive(loop) && */
+      (uv__has_active_handles(loop) || uv__has_active_reqs(loop)) &&
+      QUEUE_EMPTY(&loop->pending_queue) &&
+      QUEUE_EMPTY(&loop->idle_handles) &&
+      loop->closing_handles == NULL)
+    return uv__next_timeout(loop);
+  return 0;
+}
+
+
+int uv_backend_timeout(const uv_loop_t* loop) {
+  if (QUEUE_EMPTY(&loop->watcher_queue))
+    return uv__backend_timeout(loop);
+  /* Need to call uv_run to update the backend fd state. */
+  return 0;
+}
+
+
+int uv_loop_alive(const uv_loop_t* loop) {
+  return uv__loop_alive(loop);
+}
+
+
+int uv_run(uv_loop_t* loop, uv_run_mode mode) {
+  int timeout;
+  int r;
+  int can_sleep;
+
+  r = uv__loop_alive(loop);
+  if (!r)
+    uv__update_time(loop);
+
+  while (r != 0 && loop->stop_flag == 0) {
+    uv__update_time(loop);
+    uv__run_timers(loop);
+
+    can_sleep =
+        QUEUE_EMPTY(&loop->pending_queue) && QUEUE_EMPTY(&loop->idle_handles);
+
+    uv__run_pending(loop);
+    uv__run_idle(loop);
+    uv__run_prepare(loop);
+
+    timeout = 0;
+    if ((mode == UV_RUN_ONCE && can_sleep) || mode == UV_RUN_DEFAULT)
+      timeout = uv__backend_timeout(loop);
+
+    uv__io_poll(loop, timeout);
+
+    /* Process immediate callbacks (e.g. write_cb) a small fixed number of
+     * times to avoid loop starvation.*/
+    for (r = 0; r < 8 && !QUEUE_EMPTY(&loop->pending_queue); r++)
+      uv__run_pending(loop);
+
+    /* Run one final update on the provider_idle_time in case uv__io_poll
+     * returned because the timeout expired, but no events were received. This
+     * call will be ignored if the provider_entry_time was either never set (if
+     * the timeout == 0) or was already updated b/c an event was received.
+     */
+    uv__metrics_update_idle_time(loop);
+
+    uv__run_check(loop);
+    uv__run_closing_handles(loop);
+
+    if (mode == UV_RUN_ONCE) {
+      /* UV_RUN_ONCE implies forward progress: at least one callback must have
+       * been invoked when it returns. uv__io_poll() can return without doing
+       * I/O (meaning: no callbacks) when its timeout expires - which means we
+       * have pending timers that satisfy the forward progress constraint.
+       *
+       * UV_RUN_NOWAIT makes no guarantees about progress so it's omitted from
+       * the check.
+       */
+      uv__update_time(loop);
+      uv__run_timers(loop);
+    }
+
+    r = uv__loop_alive(loop);
+    if (mode == UV_RUN_ONCE || mode == UV_RUN_NOWAIT)
+      break;
+  }
+
+  /* The if statement lets gcc compile it to a conditional store. Avoids
+   * dirtying a cache line.
+   */
+  if (loop->stop_flag != 0)
+    loop->stop_flag = 0;
+
+  return r;
+}
+
+
+void uv_update_time(uv_loop_t* loop) {
+  uv__update_time(loop);
+}
+
+
+int uv_is_active(const uv_handle_t* handle) {
+  return uv__is_active(handle);
+}
+
+
+/* Open a socket in non-blocking close-on-exec mode, atomically if possible. */
+int uv__socket(int domain, int type, int protocol) {
+  int sockfd;
+  int err;
+
+#if defined(SOCK_NONBLOCK) && defined(SOCK_CLOEXEC)
+  sockfd = socket(domain, type | SOCK_NONBLOCK | SOCK_CLOEXEC, protocol);
+  if (sockfd != -1)
+    return sockfd;
+
+  if (errno != EINVAL)
+    return UV__ERR(errno);
+#endif
+
+  sockfd = socket(domain, type, protocol);
+  if (sockfd == -1)
+    return UV__ERR(errno);
+
+  err = uv__nonblock(sockfd, 1);
+  if (err == 0)
+    err = uv__cloexec(sockfd, 1);
+
+  if (err) {
+    uv__close(sockfd);
+    return err;
+  }
+
+#if defined(SO_NOSIGPIPE)
+  {
+    int on = 1;
+    setsockopt(sockfd, SOL_SOCKET, SO_NOSIGPIPE, &on, sizeof(on));
+  }
+#endif
+
+  return sockfd;
+}
+
+/* get a file pointer to a file in read-only and close-on-exec mode */
+FILE* uv__open_file(const char* path) {
+  int fd;
+  FILE* fp;
+
+  fd = uv__open_cloexec(path, O_RDONLY);
+  if (fd < 0)
+    return NULL;
+
+   fp = fdopen(fd, "r");
+   if (fp == NULL)
+     uv__close(fd);
+
+   return fp;
+}
+
+
+int uv__accept(int sockfd) {
+  int peerfd;
+  int err;
+
+  (void) &err;
+  assert(sockfd >= 0);
+
+  do
+#ifdef uv__accept4
+    peerfd = uv__accept4(sockfd, NULL, NULL, SOCK_NONBLOCK|SOCK_CLOEXEC);
+#else
+    peerfd = accept(sockfd, NULL, NULL);
+#endif
+  while (peerfd == -1 && errno == EINTR);
+
+  if (peerfd == -1)
+    return UV__ERR(errno);
+
+#ifndef uv__accept4
+  err = uv__cloexec(peerfd, 1);
+  if (err == 0)
+    err = uv__nonblock(peerfd, 1);
+
+  if (err != 0) {
+    uv__close(peerfd);
+    return err;
+  }
+#endif
+
+  return peerfd;
+}
+
+
+#if defined(__APPLE__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wdollar-in-identifier-extension"
+#if defined(__LP64__)
+  extern "C" int close$NOCANCEL(int);
+#else
+  extern "C" int close$NOCANCEL$UNIX2003(int);
+#endif
+#pragma GCC diagnostic pop
+#endif
+
+/* close() on macos has the "interesting" quirk that it fails with EINTR
+ * without closing the file descriptor when a thread is in the cancel state.
+ * That's why libuv calls close$NOCANCEL() instead.
+ *
+ * glibc on linux has a similar issue: close() is a cancellation point and
+ * will unwind the thread when it's in the cancel state. Work around that
+ * by making the system call directly. Musl libc is unaffected.
+ */
+int uv__close_nocancel(int fd) {
+#if defined(__APPLE__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wdollar-in-identifier-extension"
+#if defined(__LP64__) || TARGET_OS_IPHONE
+  return close$NOCANCEL(fd);
+#else
+  return close$NOCANCEL$UNIX2003(fd);
+#endif
+#pragma GCC diagnostic pop
+#elif defined(__linux__) && defined(__SANITIZE_THREAD__) && defined(__clang__)
+  long rc;
+  __sanitizer_syscall_pre_close(fd);
+  rc = syscall(SYS_close, fd);
+  __sanitizer_syscall_post_close(rc, fd);
+  return rc;
+#elif defined(__linux__) && !defined(__SANITIZE_THREAD__)
+  return syscall(SYS_close, fd);
+#else
+  return close(fd);
+#endif
+}
+
+
+int uv__close_nocheckstdio(int fd) {
+  int saved_errno;
+  int rc;
+
+  assert(fd > -1);  /* Catch uninitialized io_watcher.fd bugs. */
+
+  saved_errno = errno;
+  rc = uv__close_nocancel(fd);
+  if (rc == -1) {
+    rc = UV__ERR(errno);
+    if (rc == UV_EINTR || rc == UV__ERR(EINPROGRESS))
+      rc = 0;    /* The close is in progress, not an error. */
+    errno = saved_errno;
+  }
+
+  return rc;
+}
+
+
+int uv__close(int fd) {
+  assert(fd > STDERR_FILENO);  /* Catch stdio close bugs. */
+#if defined(__MVS__)
+  SAVE_ERRNO(epoll_file_close(fd));
+#endif
+  return uv__close_nocheckstdio(fd);
+}
+
+#if UV__NONBLOCK_IS_IOCTL
+int uv__nonblock_ioctl(int fd, int set) {
+  int r;
+
+  do
+    r = ioctl(fd, FIONBIO, &set);
+  while (r == -1 && errno == EINTR);
+
+  if (r)
+    return UV__ERR(errno);
+
+  return 0;
+}
+#endif
+
+
+int uv__nonblock_fcntl(int fd, int set) {
+  int flags;
+  int r;
+
+  do
+    r = fcntl(fd, F_GETFL);
+  while (r == -1 && errno == EINTR);
+
+  if (r == -1)
+    return UV__ERR(errno);
+
+  /* Bail out now if already set/clear. */
+  if (!!(r & O_NONBLOCK) == !!set)
+    return 0;
+
+  if (set)
+    flags = r | O_NONBLOCK;
+  else
+    flags = r & ~O_NONBLOCK;
+
+  do
+    r = fcntl(fd, F_SETFL, flags);
+  while (r == -1 && errno == EINTR);
+
+  if (r)
+    return UV__ERR(errno);
+
+  return 0;
+}
+
+
+int uv__cloexec(int fd, int set) {
+  int flags;
+  int r;
+
+  flags = 0;
+  if (set)
+    flags = FD_CLOEXEC;
+
+  do
+    r = fcntl(fd, F_SETFD, flags);
+  while (r == -1 && errno == EINTR);
+
+  if (r)
+    return UV__ERR(errno);
+
+  return 0;
+}
+
+
+ssize_t uv__recvmsg(int fd, struct msghdr* msg, int flags) {
+#if defined(__ANDROID__)   || \
+    defined(__DragonFly__) || \
+    defined(__FreeBSD__)   || \
+    defined(__NetBSD__)    || \
+    defined(__OpenBSD__)   || \
+    defined(__linux__)
+  ssize_t rc;
+  rc = recvmsg(fd, msg, flags | MSG_CMSG_CLOEXEC);
+  if (rc == -1)
+    return UV__ERR(errno);
+  return rc;
+#else
+  struct cmsghdr* cmsg;
+  int* pfd;
+  int* end;
+  ssize_t rc;
+  rc = recvmsg(fd, msg, flags);
+  if (rc == -1)
+    return UV__ERR(errno);
+  if (msg->msg_controllen == 0)
+    return rc;
+  for (cmsg = CMSG_FIRSTHDR(msg); cmsg != NULL; cmsg = CMSG_NXTHDR(msg, cmsg))
+    if (cmsg->cmsg_type == SCM_RIGHTS)
+      for (pfd = (int*) CMSG_DATA(cmsg),
+           end = (int*) ((char*) cmsg + cmsg->cmsg_len);
+           pfd < end;
+           pfd += 1)
+        uv__cloexec(*pfd, 1);
+  return rc;
+#endif
+}
+
+
+int uv_cwd(char* buffer, size_t* size) {
+  char scratch[1 + UV__PATH_MAX];
+
+  if (buffer == NULL || size == NULL)
+    return UV_EINVAL;
+
+  /* Try to read directly into the user's buffer first... */
+  if (getcwd(buffer, *size) != NULL)
+    goto fixup;
+
+  if (errno != ERANGE)
+    return UV__ERR(errno);
+
+  /* ...or into scratch space if the user's buffer is too small
+   * so we can report how much space to provide on the next try.
+   */
+  if (getcwd(scratch, sizeof(scratch)) == NULL)
+    return UV__ERR(errno);
+
+  buffer = scratch;
+
+fixup:
+
+  *size = strlen(buffer);
+
+  if (*size > 1 && buffer[*size - 1] == '/') {
+    *size -= 1;
+    buffer[*size] = '\0';
+  }
+
+  if (buffer == scratch) {
+    *size += 1;
+    return UV_ENOBUFS;
+  }
+
+  return 0;
+}
+
+
+int uv_chdir(const char* dir) {
+  if (chdir(dir))
+    return UV__ERR(errno);
+
+  return 0;
+}
+
+
+void uv_disable_stdio_inheritance(void) {
+  int fd;
+
+  /* Set the CLOEXEC flag on all open descriptors. Unconditionally try the
+   * first 16 file descriptors. After that, bail out after the first error.
+   */
+  for (fd = 0; ; fd++)
+    if (uv__cloexec(fd, 1) && fd > 15)
+      break;
+}
+
+
+int uv_fileno(const uv_handle_t* handle, uv_os_fd_t* fd) {
+  int fd_out;
+
+  switch (handle->type) {
+  case UV_TCP:
+  case UV_NAMED_PIPE:
+  case UV_TTY:
+    fd_out = uv__stream_fd((uv_stream_t*) handle);
+    break;
+
+  case UV_UDP:
+    fd_out = ((uv_udp_t *) handle)->io_watcher.fd;
+    break;
+
+  case UV_POLL:
+    fd_out = ((uv_poll_t *) handle)->io_watcher.fd;
+    break;
+
+  default:
+    return UV_EINVAL;
+  }
+
+  if (uv__is_closing(handle) || fd_out == -1)
+    return UV_EBADF;
+
+  *fd = fd_out;
+  return 0;
+}
+
+
+static void uv__run_pending(uv_loop_t* loop) {
+  QUEUE* q;
+  QUEUE pq;
+  uv__io_t* w;
+
+  QUEUE_MOVE(&loop->pending_queue, &pq);
+
+  while (!QUEUE_EMPTY(&pq)) {
+    q = QUEUE_HEAD(&pq);
+    QUEUE_REMOVE(q);
+    QUEUE_INIT(q);
+    w = QUEUE_DATA(q, uv__io_t, pending_queue);
+    w->cb(loop, w, POLLOUT);
+  }
+}
+
+
+static unsigned int next_power_of_two(unsigned int val) {
+  val -= 1;
+  val |= val >> 1;
+  val |= val >> 2;
+  val |= val >> 4;
+  val |= val >> 8;
+  val |= val >> 16;
+  val += 1;
+  return val;
+}
+
+static void maybe_resize(uv_loop_t* loop, unsigned int len) {
+  void** watchers;
+  void* fake_watcher_list;
+  void* fake_watcher_count;
+  unsigned int nwatchers;
+  unsigned int i;
+
+  if (len <= loop->nwatchers)
+    return;
+
+  /* Preserve fake watcher list and count at the end of the watchers */
+  if (loop->watchers != NULL) {
+    fake_watcher_list = loop->watchers[loop->nwatchers];
+    fake_watcher_count = loop->watchers[loop->nwatchers + 1];
+  } else {
+    fake_watcher_list = NULL;
+    fake_watcher_count = NULL;
+  }
+
+  nwatchers = next_power_of_two(len + 2) - 2;
+  watchers = (void**)
+      uv__reallocf(loop->watchers, (nwatchers + 2) * sizeof(loop->watchers[0]));
+
+  if (watchers == NULL)
+    abort();
+  for (i = loop->nwatchers; i < nwatchers; i++)
+    watchers[i] = NULL;
+  watchers[nwatchers] = fake_watcher_list;
+  watchers[nwatchers + 1] = fake_watcher_count;
+
+  loop->watchers = watchers;
+  loop->nwatchers = nwatchers;
+}
+
+
+void uv__io_init(uv__io_t* w, uv__io_cb cb, int fd) {
+  assert(cb != NULL);
+  assert(fd >= -1);
+  QUEUE_INIT(&w->pending_queue);
+  QUEUE_INIT(&w->watcher_queue);
+  w->cb = cb;
+  w->fd = fd;
+  w->events = 0;
+  w->pevents = 0;
+
+#if defined(UV_HAVE_KQUEUE)
+  w->rcount = 0;
+  w->wcount = 0;
+#endif /* defined(UV_HAVE_KQUEUE) */
+}
+
+
+void uv__io_start(uv_loop_t* loop, uv__io_t* w, unsigned int events) {
+  assert(0 == (events & ~(POLLIN | POLLOUT | UV__POLLRDHUP | UV__POLLPRI)));
+  assert(0 != events);
+  assert(w->fd >= 0);
+  assert(w->fd < INT_MAX);
+
+  w->pevents |= events;
+  maybe_resize(loop, w->fd + 1);
+
+#if !defined(__sun)
+  /* The event ports backend needs to rearm all file descriptors on each and
+   * every tick of the event loop but the other backends allow us to
+   * short-circuit here if the event mask is unchanged.
+   */
+  if (w->events == w->pevents)
+    return;
+#endif
+
+  if (QUEUE_EMPTY(&w->watcher_queue))
+    QUEUE_INSERT_TAIL(&loop->watcher_queue, &w->watcher_queue);
+
+  if (loop->watchers[w->fd] == NULL) {
+    loop->watchers[w->fd] = w;
+    loop->nfds++;
+  }
+}
+
+
+void uv__io_stop(uv_loop_t* loop, uv__io_t* w, unsigned int events) {
+  assert(0 == (events & ~(POLLIN | POLLOUT | UV__POLLRDHUP | UV__POLLPRI)));
+  assert(0 != events);
+
+  if (w->fd == -1)
+    return;
+
+  assert(w->fd >= 0);
+
+  /* Happens when uv__io_stop() is called on a handle that was never started. */
+  if ((unsigned) w->fd >= loop->nwatchers)
+    return;
+
+  w->pevents &= ~events;
+
+  if (w->pevents == 0) {
+    QUEUE_REMOVE(&w->watcher_queue);
+    QUEUE_INIT(&w->watcher_queue);
+    w->events = 0;
+
+    if (w == loop->watchers[w->fd]) {
+      assert(loop->nfds > 0);
+      loop->watchers[w->fd] = NULL;
+      loop->nfds--;
+    }
+  }
+  else if (QUEUE_EMPTY(&w->watcher_queue))
+    QUEUE_INSERT_TAIL(&loop->watcher_queue, &w->watcher_queue);
+}
+
+
+void uv__io_close(uv_loop_t* loop, uv__io_t* w) {
+  uv__io_stop(loop, w, POLLIN | POLLOUT | UV__POLLRDHUP | UV__POLLPRI);
+  QUEUE_REMOVE(&w->pending_queue);
+
+  /* Remove stale events for this file descriptor */
+  if (w->fd != -1)
+    uv__platform_invalidate_fd(loop, w->fd);
+}
+
+
+void uv__io_feed(uv_loop_t* loop, uv__io_t* w) {
+  if (QUEUE_EMPTY(&w->pending_queue))
+    QUEUE_INSERT_TAIL(&loop->pending_queue, &w->pending_queue);
+}
+
+
+int uv__io_active(const uv__io_t* w, unsigned int events) {
+  assert(0 == (events & ~(POLLIN | POLLOUT | UV__POLLRDHUP | UV__POLLPRI)));
+  assert(0 != events);
+  return 0 != (w->pevents & events);
+}
+
+
+int uv__fd_exists(uv_loop_t* loop, int fd) {
+  return (unsigned) fd < loop->nwatchers && loop->watchers[fd] != NULL;
+}
+
+
+int uv_getrusage(uv_rusage_t* rusage) {
+  struct rusage usage;
+
+  if (getrusage(RUSAGE_SELF, &usage))
+    return UV__ERR(errno);
+
+  rusage->ru_utime.tv_sec = usage.ru_utime.tv_sec;
+  rusage->ru_utime.tv_usec = usage.ru_utime.tv_usec;
+
+  rusage->ru_stime.tv_sec = usage.ru_stime.tv_sec;
+  rusage->ru_stime.tv_usec = usage.ru_stime.tv_usec;
+
+#if !defined(__MVS__) && !defined(__HAIKU__)
+  rusage->ru_maxrss = usage.ru_maxrss;
+  rusage->ru_ixrss = usage.ru_ixrss;
+  rusage->ru_idrss = usage.ru_idrss;
+  rusage->ru_isrss = usage.ru_isrss;
+  rusage->ru_minflt = usage.ru_minflt;
+  rusage->ru_majflt = usage.ru_majflt;
+  rusage->ru_nswap = usage.ru_nswap;
+  rusage->ru_inblock = usage.ru_inblock;
+  rusage->ru_oublock = usage.ru_oublock;
+  rusage->ru_msgsnd = usage.ru_msgsnd;
+  rusage->ru_msgrcv = usage.ru_msgrcv;
+  rusage->ru_nsignals = usage.ru_nsignals;
+  rusage->ru_nvcsw = usage.ru_nvcsw;
+  rusage->ru_nivcsw = usage.ru_nivcsw;
+#endif
+
+  return 0;
+}
+
+
+int uv__open_cloexec(const char* path, int flags) {
+#if defined(O_CLOEXEC)
+  int fd;
+
+  fd = open(path, flags | O_CLOEXEC);
+  if (fd == -1)
+    return UV__ERR(errno);
+
+  return fd;
+#else  /* O_CLOEXEC */
+  int err;
+  int fd;
+
+  fd = open(path, flags);
+  if (fd == -1)
+    return UV__ERR(errno);
+
+  err = uv__cloexec(fd, 1);
+  if (err) {
+    uv__close(fd);
+    return err;
+  }
+
+  return fd;
+#endif  /* O_CLOEXEC */
+}
+
+
+int uv__slurp(const char* filename, char* buf, size_t len) {
+  ssize_t n;
+  int fd;
+
+  assert(len > 0);
+
+  fd = uv__open_cloexec(filename, O_RDONLY);
+  if (fd < 0)
+    return fd;
+
+  do
+    n = read(fd, buf, len - 1);
+  while (n == -1 && errno == EINTR);
+
+  if (uv__close_nocheckstdio(fd))
+    abort();
+
+  if (n < 0)
+    return UV__ERR(errno);
+
+  buf[n] = '\0';
+
+  return 0;
+}
+
+
+int uv__dup2_cloexec(int oldfd, int newfd) {
+#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__linux__)
+  int r;
+
+  r = dup3(oldfd, newfd, O_CLOEXEC);
+  if (r == -1)
+    return UV__ERR(errno);
+
+  return r;
+#else
+  int err;
+  int r;
+
+  r = dup2(oldfd, newfd);  /* Never retry. */
+  if (r == -1)
+    return UV__ERR(errno);
+
+  err = uv__cloexec(newfd, 1);
+  if (err != 0) {
+    uv__close(newfd);
+    return err;
+  }
+
+  return r;
+#endif
+}
+
+
+int uv_os_homedir(char* buffer, size_t* size) {
+  uv_passwd_t pwd;
+  size_t len;
+  int r;
+
+  /* Check if the HOME environment variable is set first. The task of
+     performing input validation on buffer and size is taken care of by
+     uv_os_getenv(). */
+  r = uv_os_getenv("HOME", buffer, size);
+
+  if (r != UV_ENOENT)
+    return r;
+
+  /* HOME is not set, so call uv__getpwuid_r() */
+  r = uv__getpwuid_r(&pwd);
+
+  if (r != 0) {
+    return r;
+  }
+
+  len = strlen(pwd.homedir);
+
+  if (len >= *size) {
+    *size = len + 1;
+    uv_os_free_passwd(&pwd);
+    return UV_ENOBUFS;
+  }
+
+  memcpy(buffer, pwd.homedir, len + 1);
+  *size = len;
+  uv_os_free_passwd(&pwd);
+
+  return 0;
+}
+
+
+int uv_os_tmpdir(char* buffer, size_t* size) {
+  const char* buf;
+  size_t len;
+
+  if (buffer == NULL || size == NULL || *size == 0)
+    return UV_EINVAL;
+
+#define CHECK_ENV_VAR(name)                                                   \
+  do {                                                                        \
+    buf = getenv(name);                                                       \
+    if (buf != NULL)                                                          \
+      goto return_buffer;                                                     \
+  }                                                                           \
+  while (0)
+
+  /* Check the TMPDIR, TMP, TEMP, and TEMPDIR environment variables in order */
+  CHECK_ENV_VAR("TMPDIR");
+  CHECK_ENV_VAR("TMP");
+  CHECK_ENV_VAR("TEMP");
+  CHECK_ENV_VAR("TEMPDIR");
+
+#undef CHECK_ENV_VAR
+
+  /* No temp environment variables defined */
+  #if defined(__ANDROID__)
+    buf = "/data/local/tmp";
+  #else
+    buf = "/tmp";
+  #endif
+
+return_buffer:
+  len = strlen(buf);
+
+  if (len >= *size) {
+    *size = len + 1;
+    return UV_ENOBUFS;
+  }
+
+  /* The returned directory should not have a trailing slash. */
+  if (len > 1 && buf[len - 1] == '/') {
+    len--;
+  }
+
+  memcpy(buffer, buf, len + 1);
+  buffer[len] = '\0';
+  *size = len;
+
+  return 0;
+}
+
+
+int uv__getpwuid_r(uv_passwd_t* pwd) {
+  struct passwd pw;
+  struct passwd* result;
+  char* buf;
+  uid_t uid;
+  size_t bufsize;
+  size_t name_size;
+  size_t homedir_size;
+  size_t shell_size;
+  int r;
+
+  if (pwd == NULL)
+    return UV_EINVAL;
+
+  uid = geteuid();
+
+  /* Calling sysconf(_SC_GETPW_R_SIZE_MAX) would get the suggested size, but it
+   * is frequently 1024 or 4096, so we can just use that directly. The pwent
+   * will not usually be large. */
+  for (bufsize = 2000;; bufsize *= 2) {
+    buf = (char*)uv__malloc(bufsize);
+
+    if (buf == NULL)
+      return UV_ENOMEM;
+
+    do
+      r = getpwuid_r(uid, &pw, buf, bufsize, &result);
+    while (r == EINTR);
+
+    if (r != 0 || result == NULL)
+      uv__free(buf);
+
+    if (r != ERANGE)
+      break;
+  }
+
+  if (r != 0)
+    return UV__ERR(r);
+
+  if (result == NULL)
+    return UV_ENOENT;
+
+  /* Allocate memory for the username, shell, and home directory */
+  name_size = strlen(pw.pw_name) + 1;
+  homedir_size = strlen(pw.pw_dir) + 1;
+  shell_size = strlen(pw.pw_shell) + 1;
+  pwd->username = (char*)uv__malloc(name_size + homedir_size + shell_size);
+
+  if (pwd->username == NULL) {
+    uv__free(buf);
+    return UV_ENOMEM;
+  }
+
+  /* Copy the username */
+  memcpy(pwd->username, pw.pw_name, name_size);
+
+  /* Copy the home directory */
+  pwd->homedir = pwd->username + name_size;
+  memcpy(pwd->homedir, pw.pw_dir, homedir_size);
+
+  /* Copy the shell */
+  pwd->shell = pwd->homedir + homedir_size;
+  memcpy(pwd->shell, pw.pw_shell, shell_size);
+
+  /* Copy the uid and gid */
+  pwd->uid = pw.pw_uid;
+  pwd->gid = pw.pw_gid;
+
+  uv__free(buf);
+
+  return 0;
+}
+
+
+void uv_os_free_passwd(uv_passwd_t* pwd) {
+  if (pwd == NULL)
+    return;
+
+  /*
+    The memory for name, shell, and homedir are allocated in a single
+    uv__malloc() call. The base of the pointer is stored in pwd->username, so
+    that is the field that needs to be freed.
+  */
+  uv__free(pwd->username);
+  pwd->username = NULL;
+  pwd->shell = NULL;
+  pwd->homedir = NULL;
+}
+
+
+int uv_os_get_passwd(uv_passwd_t* pwd) {
+  return uv__getpwuid_r(pwd);
+}
+
+
+int uv_translate_sys_error(int sys_errno) {
+  /* If < 0 then it's already a libuv error. */
+  return sys_errno <= 0 ? sys_errno : -sys_errno;
+}
+
+
+int uv_os_environ(uv_env_item_t** envitems, int* count) {
+  int i, j, cnt;
+  uv_env_item_t* envitem;
+
+  *envitems = NULL;
+  *count = 0;
+
+  for (i = 0; environ[i] != NULL; i++);
+
+  *envitems = (uv_env_item_s*)uv__calloc(i, sizeof(**envitems));
+
+  if (*envitems == NULL)
+    return UV_ENOMEM;
+
+  for (j = 0, cnt = 0; j < i; j++) {
+    char* buf;
+    char* ptr;
+
+    if (environ[j] == NULL)
+      break;
+
+    buf = uv__strdup(environ[j]);
+    if (buf == NULL)
+      goto fail;
+
+    ptr = strchr(buf, '=');
+    if (ptr == NULL) {
+      uv__free(buf);
+      continue;
+    }
+
+    *ptr = '\0';
+
+    envitem = &(*envitems)[cnt];
+    envitem->name = buf;
+    envitem->value = ptr + 1;
+
+    cnt++;
+  }
+
+  *count = cnt;
+  return 0;
+
+fail:
+  for (i = 0; i < cnt; i++) {
+    envitem = &(*envitems)[cnt];
+    uv__free(envitem->name);
+  }
+  uv__free(*envitems);
+
+  *envitems = NULL;
+  *count = 0;
+  return UV_ENOMEM;
+}
+
+
+int uv_os_getenv(const char* name, char* buffer, size_t* size) {
+  char* var;
+  size_t len;
+
+  if (name == NULL || buffer == NULL || size == NULL || *size == 0)
+    return UV_EINVAL;
+
+  var = getenv(name);
+
+  if (var == NULL)
+    return UV_ENOENT;
+
+  len = strlen(var);
+
+  if (len >= *size) {
+    *size = len + 1;
+    return UV_ENOBUFS;
+  }
+
+  memcpy(buffer, var, len + 1);
+  *size = len;
+
+  return 0;
+}
+
+
+int uv_os_setenv(const char* name, const char* value) {
+  if (name == NULL || value == NULL)
+    return UV_EINVAL;
+
+  if (setenv(name, value, 1) != 0)
+    return UV__ERR(errno);
+
+  return 0;
+}
+
+
+int uv_os_unsetenv(const char* name) {
+  if (name == NULL)
+    return UV_EINVAL;
+
+  if (unsetenv(name) != 0)
+    return UV__ERR(errno);
+
+  return 0;
+}
+
+
+int uv_os_gethostname(char* buffer, size_t* size) {
+  /*
+    On some platforms, if the input buffer is not large enough, gethostname()
+    succeeds, but truncates the result. libuv can detect this and return ENOBUFS
+    instead by creating a large enough buffer and comparing the hostname length
+    to the size input.
+  */
+  char buf[UV_MAXHOSTNAMESIZE];
+  size_t len;
+
+  if (buffer == NULL || size == NULL || *size == 0)
+    return UV_EINVAL;
+
+  if (gethostname(buf, sizeof(buf)) != 0)
+    return UV__ERR(errno);
+
+  buf[sizeof(buf) - 1] = '\0'; /* Null terminate, just to be safe. */
+  len = strlen(buf);
+
+  if (len >= *size) {
+    *size = len + 1;
+    return UV_ENOBUFS;
+  }
+
+  memcpy(buffer, buf, len + 1);
+  *size = len;
+  return 0;
+}
+
+
+uv_os_fd_t uv_get_osfhandle(int fd) {
+  return fd;
+}
+
+int uv_open_osfhandle(uv_os_fd_t os_fd) {
+  return os_fd;
+}
+
+uv_pid_t uv_os_getpid(void) {
+  return getpid();
+}
+
+
+uv_pid_t uv_os_getppid(void) {
+  return getppid();
+}
+
+
+int uv_os_getpriority(uv_pid_t pid, int* priority) {
+  int r;
+
+  if (priority == NULL)
+    return UV_EINVAL;
+
+  errno = 0;
+  r = getpriority(PRIO_PROCESS, (int) pid);
+
+  if (r == -1 && errno != 0)
+    return UV__ERR(errno);
+
+  *priority = r;
+  return 0;
+}
+
+
+int uv_os_setpriority(uv_pid_t pid, int priority) {
+  if (priority < UV_PRIORITY_HIGHEST || priority > UV_PRIORITY_LOW)
+    return UV_EINVAL;
+
+  if (setpriority(PRIO_PROCESS, (int) pid, priority) != 0)
+    return UV__ERR(errno);
+
+  return 0;
+}
+
+
+int uv_os_uname(uv_utsname_t* buffer) {
+  struct utsname buf;
+  int r;
+
+  if (buffer == NULL)
+    return UV_EINVAL;
+
+  if (uname(&buf) == -1) {
+    r = UV__ERR(errno);
+    goto error;
+  }
+
+  r = uv__strscpy(buffer->sysname, buf.sysname, sizeof(buffer->sysname));
+  if (r == UV_E2BIG)
+    goto error;
+
+#ifdef _AIX
+  r = snprintf(buffer->release,
+               sizeof(buffer->release),
+               "%s.%s",
+               buf.version,
+               buf.release);
+  if (r >= sizeof(buffer->release)) {
+    r = UV_E2BIG;
+    goto error;
+  }
+#else
+  r = uv__strscpy(buffer->release, buf.release, sizeof(buffer->release));
+  if (r == UV_E2BIG)
+    goto error;
+#endif
+
+  r = uv__strscpy(buffer->version, buf.version, sizeof(buffer->version));
+  if (r == UV_E2BIG)
+    goto error;
+
+#if defined(_AIX) || defined(__PASE__)
+  r = uv__strscpy(buffer->machine, "ppc64", sizeof(buffer->machine));
+#else
+  r = uv__strscpy(buffer->machine, buf.machine, sizeof(buffer->machine));
+#endif
+
+  if (r == UV_E2BIG)
+    goto error;
+
+  return 0;
+
+error:
+  buffer->sysname[0] = '\0';
+  buffer->release[0] = '\0';
+  buffer->version[0] = '\0';
+  buffer->machine[0] = '\0';
+  return r;
+}
+
+int uv__getsockpeername(const uv_handle_t* handle,
+                        uv__peersockfunc func,
+                        struct sockaddr* name,
+                        int* namelen) {
+  socklen_t socklen;
+  uv_os_fd_t fd;
+  int r;
+
+  r = uv_fileno(handle, &fd);
+  if (r < 0)
+    return r;
+
+  /* sizeof(socklen_t) != sizeof(int) on some systems. */
+  socklen = (socklen_t) *namelen;
+
+  if (func(fd, name, &socklen))
+    return UV__ERR(errno);
+
+  *namelen = (int) socklen;
+  return 0;
+}
+
+int uv_gettimeofday(uv_timeval64_t* tv) {
+  struct timeval time;
+
+  if (tv == NULL)
+    return UV_EINVAL;
+
+  if (gettimeofday(&time, NULL) != 0)
+    return UV__ERR(errno);
+
+  tv->tv_sec = (int64_t) time.tv_sec;
+  tv->tv_usec = (int32_t) time.tv_usec;
+  return 0;
+}
+
+void uv_sleep(unsigned int msec) {
+  struct timespec timeout;
+  int rc;
+
+  timeout.tv_sec = msec / 1000;
+  timeout.tv_nsec = (msec % 1000) * 1000 * 1000;
+
+  do
+    rc = nanosleep(&timeout, &timeout);
+  while (rc == -1 && errno == EINTR);
+
+  assert(rc == 0);
+}
+
+int uv__search_path(const char* prog, char* buf, size_t* buflen) {
+  char abspath[UV__PATH_MAX];
+  size_t abspath_size;
+  char trypath[UV__PATH_MAX];
+  char* cloned_path;
+  char* path_env;
+  char* token;
+  char* itr;
+
+  if (buf == NULL || buflen == NULL || *buflen == 0)
+    return UV_EINVAL;
+
+  /*
+   * Possibilities for prog:
+   * i) an absolute path such as: /home/user/myprojects/nodejs/node
+   * ii) a relative path such as: ./node or ../myprojects/nodejs/node
+   * iii) a bare filename such as "node", after exporting PATH variable
+   *     to its location.
+   */
+
+  /* Case i) and ii) absolute or relative paths */
+  if (strchr(prog, '/') != NULL) {
+    if (realpath(prog, abspath) != abspath)
+      return UV__ERR(errno);
+
+    abspath_size = strlen(abspath);
+
+    *buflen -= 1;
+    if (*buflen > abspath_size)
+      *buflen = abspath_size;
+
+    memcpy(buf, abspath, *buflen);
+    buf[*buflen] = '\0';
+
+    return 0;
+  }
+
+  /* Case iii). Search PATH environment variable */
+  cloned_path = NULL;
+  token = NULL;
+  path_env = getenv("PATH");
+
+  if (path_env == NULL)
+    return UV_EINVAL;
+
+  cloned_path = uv__strdup(path_env);
+  if (cloned_path == NULL)
+    return UV_ENOMEM;
+
+  token = uv__strtok(cloned_path, ":", &itr);
+  while (token != NULL) {
+    snprintf(trypath, sizeof(trypath) - 1, "%s/%s", token, prog);
+    if (realpath(trypath, abspath) == abspath) {
+      /* Check the match is executable */
+      if (access(abspath, X_OK) == 0) {
+        abspath_size = strlen(abspath);
+
+        *buflen -= 1;
+        if (*buflen > abspath_size)
+          *buflen = abspath_size;
+
+        memcpy(buf, abspath, *buflen);
+        buf[*buflen] = '\0';
+
+        uv__free(cloned_path);
+        return 0;
+      }
+    }
+    token = uv__strtok(NULL, ":", &itr);
+  }
+  uv__free(cloned_path);
+
+  /* Out of tokens (path entries), and no match found */
+  return UV_EINVAL;
+}
+
+
+unsigned int uv_available_parallelism(void) {
+#ifdef __linux__
+  cpu_set_t set;
+  long rc;
+
+  memset(&set, 0, sizeof(set));
+
+  /* sysconf(_SC_NPROCESSORS_ONLN) in musl calls sched_getaffinity() but in
+   * glibc it's... complicated... so for consistency try sched_getaffinity()
+   * before falling back to sysconf(_SC_NPROCESSORS_ONLN).
+   */
+  if (0 == sched_getaffinity(0, sizeof(set), &set))
+    rc = CPU_COUNT(&set);
+  else
+    rc = sysconf(_SC_NPROCESSORS_ONLN);
+
+  if (rc < 1)
+    rc = 1;
+
+  return (unsigned) rc;
+#elif defined(__MVS__)
+  int rc;
+
+  rc = __get_num_online_cpus();
+  if (rc < 1)
+    rc = 1;
+
+  return (unsigned) rc;
+#else  /* __linux__ */
+  long rc;
+
+  rc = sysconf(_SC_NPROCESSORS_ONLN);
+  if (rc < 1)
+    rc = 1;
+
+  return (unsigned) rc;
+#endif  /* __linux__ */
+}
diff --git a/wpinet/src/main/native/thirdparty/libuv/src/unix/cygwin.cpp b/wpinet/src/main/native/thirdparty/libuv/src/unix/cygwin.cpp
new file mode 100644
index 0000000..169958d
--- /dev/null
+++ b/wpinet/src/main/native/thirdparty/libuv/src/unix/cygwin.cpp
@@ -0,0 +1,53 @@
+/* Copyright libuv project contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include "uv.h"
+#include "internal.h"
+
+#include <sys/sysinfo.h>
+#include <unistd.h>
+
+int uv_uptime(double* uptime) {
+  struct sysinfo info;
+
+  if (sysinfo(&info) < 0)
+    return UV__ERR(errno);
+
+  *uptime = info.uptime;
+  return 0;
+}
+
+int uv_resident_set_memory(size_t* rss) {
+  /* FIXME: read /proc/meminfo? */
+  *rss = 0;
+  return 0;
+}
+
+int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) {
+  /* FIXME: read /proc/stat? */
+  *cpu_infos = NULL;
+  *count = 0;
+  return UV_ENOSYS;
+}
+
+uint64_t uv_get_constrained_memory(void) {
+  return 0;  /* Memory constraints are unknown. */
+}
diff --git a/wpinet/src/main/native/thirdparty/libuv/src/unix/darwin-proctitle.cpp b/wpinet/src/main/native/thirdparty/libuv/src/unix/darwin-proctitle.cpp
new file mode 100644
index 0000000..9bd55dd
--- /dev/null
+++ b/wpinet/src/main/native/thirdparty/libuv/src/unix/darwin-proctitle.cpp
@@ -0,0 +1,193 @@
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include "uv.h"
+#include "internal.h"
+
+#include <dlfcn.h>
+#include <errno.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <TargetConditionals.h>
+
+#if !TARGET_OS_IPHONE
+#include "darwin-stub.h"
+#endif
+
+
+static int uv__pthread_setname_np(const char* name) {
+  char namebuf[64];  /* MAXTHREADNAMESIZE */
+  int err;
+
+  strncpy(namebuf, name, sizeof(namebuf) - 1);
+  namebuf[sizeof(namebuf) - 1] = '\0';
+
+  err = pthread_setname_np(namebuf);
+  if (err)
+    return UV__ERR(err);
+
+  return 0;
+}
+
+
+int uv__set_process_title(const char* title) {
+#if TARGET_OS_IPHONE
+  return uv__pthread_setname_np(title);
+#else
+  CFStringRef (*pCFStringCreateWithCString)(CFAllocatorRef,
+                                            const char*,
+                                            CFStringEncoding);
+  CFBundleRef (*pCFBundleGetBundleWithIdentifier)(CFStringRef);
+  void *(*pCFBundleGetDataPointerForName)(CFBundleRef, CFStringRef);
+  void *(*pCFBundleGetFunctionPointerForName)(CFBundleRef, CFStringRef);
+  CFTypeRef (*pLSGetCurrentApplicationASN)(void);
+  OSStatus (*pLSSetApplicationInformationItem)(int,
+                                               CFTypeRef,
+                                               CFStringRef,
+                                               CFStringRef,
+                                               CFDictionaryRef*);
+  void* application_services_handle;
+  void* core_foundation_handle;
+  CFBundleRef launch_services_bundle;
+  CFStringRef* display_name_key;
+  CFDictionaryRef (*pCFBundleGetInfoDictionary)(CFBundleRef);
+  CFBundleRef (*pCFBundleGetMainBundle)(void);
+  CFDictionaryRef (*pLSApplicationCheckIn)(int, CFDictionaryRef);
+  void (*pLSSetApplicationLaunchServicesServerConnectionStatus)(uint64_t,
+                                                                void*);
+  CFTypeRef asn;
+  int err;
+
+  err = UV_ENOENT;
+  application_services_handle = dlopen("/System/Library/Frameworks/"
+                                       "ApplicationServices.framework/"
+                                       "Versions/A/ApplicationServices",
+                                       RTLD_LAZY | RTLD_LOCAL);
+  core_foundation_handle = dlopen("/System/Library/Frameworks/"
+                                  "CoreFoundation.framework/"
+                                  "Versions/A/CoreFoundation",
+                                  RTLD_LAZY | RTLD_LOCAL);
+
+  if (application_services_handle == NULL || core_foundation_handle == NULL)
+    goto out;
+
+  *(void **)(&pCFStringCreateWithCString) =
+      dlsym(core_foundation_handle, "CFStringCreateWithCString");
+  *(void **)(&pCFBundleGetBundleWithIdentifier) =
+      dlsym(core_foundation_handle, "CFBundleGetBundleWithIdentifier");
+  *(void **)(&pCFBundleGetDataPointerForName) =
+      dlsym(core_foundation_handle, "CFBundleGetDataPointerForName");
+  *(void **)(&pCFBundleGetFunctionPointerForName) =
+      dlsym(core_foundation_handle, "CFBundleGetFunctionPointerForName");
+
+  if (pCFStringCreateWithCString == NULL ||
+      pCFBundleGetBundleWithIdentifier == NULL ||
+      pCFBundleGetDataPointerForName == NULL ||
+      pCFBundleGetFunctionPointerForName == NULL) {
+    goto out;
+  }
+
+#define S(s) pCFStringCreateWithCString(NULL, (s), kCFStringEncodingUTF8)
+
+  launch_services_bundle =
+      pCFBundleGetBundleWithIdentifier(S("com.apple.LaunchServices"));
+
+  if (launch_services_bundle == NULL)
+    goto out;
+
+  *(void **)(&pLSGetCurrentApplicationASN) =
+      pCFBundleGetFunctionPointerForName(launch_services_bundle,
+                                         S("_LSGetCurrentApplicationASN"));
+
+  if (pLSGetCurrentApplicationASN == NULL)
+    goto out;
+
+  *(void **)(&pLSSetApplicationInformationItem) =
+      pCFBundleGetFunctionPointerForName(launch_services_bundle,
+                                         S("_LSSetApplicationInformationItem"));
+
+  if (pLSSetApplicationInformationItem == NULL)
+    goto out;
+
+  display_name_key = (CFStringRef*)
+      pCFBundleGetDataPointerForName(launch_services_bundle,
+                                     S("_kLSDisplayNameKey"));
+
+  if (display_name_key == NULL || *display_name_key == NULL)
+    goto out;
+
+  *(void **)(&pCFBundleGetInfoDictionary) = dlsym(core_foundation_handle,
+                                     "CFBundleGetInfoDictionary");
+  *(void **)(&pCFBundleGetMainBundle) = dlsym(core_foundation_handle,
+                                 "CFBundleGetMainBundle");
+  if (pCFBundleGetInfoDictionary == NULL || pCFBundleGetMainBundle == NULL)
+    goto out;
+
+  *(void **)(&pLSApplicationCheckIn) = pCFBundleGetFunctionPointerForName(
+      launch_services_bundle,
+      S("_LSApplicationCheckIn"));
+
+  if (pLSApplicationCheckIn == NULL)
+    goto out;
+
+  *(void **)(&pLSSetApplicationLaunchServicesServerConnectionStatus) =
+      pCFBundleGetFunctionPointerForName(
+          launch_services_bundle,
+          S("_LSSetApplicationLaunchServicesServerConnectionStatus"));
+
+  if (pLSSetApplicationLaunchServicesServerConnectionStatus == NULL)
+    goto out;
+
+  pLSSetApplicationLaunchServicesServerConnectionStatus(0, NULL);
+
+  /* Check into process manager?! */
+  pLSApplicationCheckIn(-2,
+                        pCFBundleGetInfoDictionary(pCFBundleGetMainBundle()));
+
+  asn = pLSGetCurrentApplicationASN();
+
+  err = UV_EBUSY;
+  if (asn == NULL)
+    goto out;
+
+  err = UV_EINVAL;
+  if (pLSSetApplicationInformationItem(-2,  /* Magic value. */
+                                       asn,
+                                       *display_name_key,
+                                       S(title),
+                                       NULL) != noErr) {
+    goto out;
+  }
+
+  uv__pthread_setname_np(title);  /* Don't care if it fails. */
+  err = 0;
+
+out:
+  if (core_foundation_handle != NULL)
+    dlclose(core_foundation_handle);
+
+  if (application_services_handle != NULL)
+    dlclose(application_services_handle);
+
+  return err;
+#endif  /* !TARGET_OS_IPHONE */
+}
diff --git a/wpinet/src/main/native/thirdparty/libuv/src/unix/darwin-stub.h b/wpinet/src/main/native/thirdparty/libuv/src/unix/darwin-stub.h
new file mode 100644
index 0000000..433e3ef
--- /dev/null
+++ b/wpinet/src/main/native/thirdparty/libuv/src/unix/darwin-stub.h
@@ -0,0 +1,113 @@
+/* Copyright libuv project contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#ifndef UV_DARWIN_STUB_H_
+#define UV_DARWIN_STUB_H_
+
+#include <stdint.h>
+
+struct CFArrayCallBacks;
+struct CFRunLoopSourceContext;
+struct FSEventStreamContext;
+struct CFRange;
+
+typedef double CFAbsoluteTime;
+typedef double CFTimeInterval;
+typedef int FSEventStreamEventFlags;
+typedef int OSStatus;
+typedef long CFIndex;
+typedef struct CFArrayCallBacks CFArrayCallBacks;
+typedef struct CFRunLoopSourceContext CFRunLoopSourceContext;
+typedef struct FSEventStreamContext FSEventStreamContext;
+typedef uint32_t FSEventStreamCreateFlags;
+typedef uint64_t FSEventStreamEventId;
+typedef unsigned CFStringEncoding;
+typedef void* CFAllocatorRef;
+typedef void* CFArrayRef;
+typedef void* CFBundleRef;
+typedef void* CFDataRef;
+typedef void* CFDictionaryRef;
+typedef void* CFMutableDictionaryRef;
+typedef struct CFRange CFRange;
+typedef void* CFRunLoopRef;
+typedef void* CFRunLoopSourceRef;
+typedef void* CFStringRef;
+typedef void* CFTypeRef;
+typedef void* FSEventStreamRef;
+
+typedef uint32_t IOOptionBits;
+typedef unsigned int io_iterator_t;
+typedef unsigned int io_object_t;
+typedef unsigned int io_service_t;
+typedef unsigned int io_registry_entry_t;
+
+
+typedef void (*FSEventStreamCallback)(const FSEventStreamRef,
+                                      void*,
+                                      size_t,
+                                      void*,
+                                      const FSEventStreamEventFlags*,
+                                      const FSEventStreamEventId*);
+
+struct CFRunLoopSourceContext {
+  CFIndex version;
+  void* info;
+  void* pad[7];
+  void (*perform)(void*);
+};
+
+struct FSEventStreamContext {
+  CFIndex version;
+  void* info;
+  void* pad[3];
+};
+
+struct CFRange {
+  CFIndex location;
+  CFIndex length;
+};
+
+static const CFStringEncoding kCFStringEncodingUTF8 = 0x8000100;
+static const OSStatus noErr = 0;
+
+static const FSEventStreamEventId kFSEventStreamEventIdSinceNow = -1;
+
+static const int kFSEventStreamCreateFlagNoDefer = 2;
+static const int kFSEventStreamCreateFlagFileEvents = 16;
+
+static const int kFSEventStreamEventFlagEventIdsWrapped = 8;
+static const int kFSEventStreamEventFlagHistoryDone = 16;
+static const int kFSEventStreamEventFlagItemChangeOwner = 0x4000;
+static const int kFSEventStreamEventFlagItemCreated = 0x100;
+static const int kFSEventStreamEventFlagItemFinderInfoMod = 0x2000;
+static const int kFSEventStreamEventFlagItemInodeMetaMod = 0x400;
+static const int kFSEventStreamEventFlagItemIsDir = 0x20000;
+static const int kFSEventStreamEventFlagItemModified = 0x1000;
+static const int kFSEventStreamEventFlagItemRemoved = 0x200;
+static const int kFSEventStreamEventFlagItemRenamed = 0x800;
+static const int kFSEventStreamEventFlagItemXattrMod = 0x8000;
+static const int kFSEventStreamEventFlagKernelDropped = 4;
+static const int kFSEventStreamEventFlagMount = 64;
+static const int kFSEventStreamEventFlagRootChanged = 32;
+static const int kFSEventStreamEventFlagUnmount = 128;
+static const int kFSEventStreamEventFlagUserDropped = 2;
+
+#endif  /* UV_DARWIN_STUB_H_ */
diff --git a/wpinet/src/main/native/thirdparty/libuv/src/unix/darwin.cpp b/wpinet/src/main/native/thirdparty/libuv/src/unix/darwin.cpp
new file mode 100644
index 0000000..ed51a6a
--- /dev/null
+++ b/wpinet/src/main/native/thirdparty/libuv/src/unix/darwin.cpp
@@ -0,0 +1,384 @@
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include "uv.h"
+#include "internal.h"
+
+#include <assert.h>
+#include <stdint.h>
+#include <errno.h>
+
+#include <dlfcn.h>
+#include <mach/mach.h>
+#include <mach/mach_time.h>
+#include <mach-o/dyld.h> /* _NSGetExecutablePath */
+#include <sys/resource.h>
+#include <sys/sysctl.h>
+#include <unistd.h>  /* sysconf */
+
+#include "darwin-stub.h"
+
+static uv_once_t once = UV_ONCE_INIT;
+static uint64_t (*time_func)(void);
+static mach_timebase_info_data_t timebase;
+
+typedef unsigned char UInt8;
+
+int uv__platform_loop_init(uv_loop_t* loop) {
+  loop->cf_state = NULL;
+
+  if (uv__kqueue_init(loop))
+    return UV__ERR(errno);
+
+  return 0;
+}
+
+
+void uv__platform_loop_delete(uv_loop_t* loop) {
+  uv__fsevents_loop_delete(loop);
+}
+
+
+static void uv__hrtime_init_once(void) {
+  if (KERN_SUCCESS != mach_timebase_info(&timebase))
+    abort();
+
+  time_func = (uint64_t (*)(void)) dlsym(RTLD_DEFAULT, "mach_continuous_time");
+  if (time_func == NULL)
+    time_func = mach_absolute_time;
+}
+
+
+uint64_t uv__hrtime(uv_clocktype_t type) {
+  uv_once(&once, uv__hrtime_init_once);
+  return time_func() * timebase.numer / timebase.denom;
+}
+
+
+int uv_exepath(char* buffer, size_t* size) {
+  /* realpath(exepath) may be > PATH_MAX so double it to be on the safe side. */
+  char abspath[PATH_MAX * 2 + 1];
+  char exepath[PATH_MAX + 1];
+  uint32_t exepath_size;
+  size_t abspath_size;
+
+  if (buffer == NULL || size == NULL || *size == 0)
+    return UV_EINVAL;
+
+  exepath_size = sizeof(exepath);
+  if (_NSGetExecutablePath(exepath, &exepath_size))
+    return UV_EIO;
+
+  if (realpath(exepath, abspath) != abspath)
+    return UV__ERR(errno);
+
+  abspath_size = strlen(abspath);
+  if (abspath_size == 0)
+    return UV_EIO;
+
+  *size -= 1;
+  if (*size > abspath_size)
+    *size = abspath_size;
+
+  memcpy(buffer, abspath, *size);
+  buffer[*size] = '\0';
+
+  return 0;
+}
+
+
+uint64_t uv_get_free_memory(void) {
+  vm_statistics_data_t info;
+  mach_msg_type_number_t count = sizeof(info) / sizeof(integer_t);
+
+  if (host_statistics(mach_host_self(), HOST_VM_INFO,
+                      (host_info_t)&info, &count) != KERN_SUCCESS) {
+    return UV_EINVAL;  /* FIXME(bnoordhuis) Translate error. */
+  }
+
+  return (uint64_t) info.free_count * sysconf(_SC_PAGESIZE);
+}
+
+
+uint64_t uv_get_total_memory(void) {
+  uint64_t info;
+  int which[] = {CTL_HW, HW_MEMSIZE};
+  size_t size = sizeof(info);
+
+  if (sysctl(which, ARRAY_SIZE(which), &info, &size, NULL, 0))
+    return UV__ERR(errno);
+
+  return (uint64_t) info;
+}
+
+
+uint64_t uv_get_constrained_memory(void) {
+  return 0;  /* Memory constraints are unknown. */
+}
+
+
+void uv_loadavg(double avg[3]) {
+  struct loadavg info;
+  size_t size = sizeof(info);
+  int which[] = {CTL_VM, VM_LOADAVG};
+
+  if (sysctl(which, ARRAY_SIZE(which), &info, &size, NULL, 0) < 0) return;
+
+  avg[0] = (double) info.ldavg[0] / info.fscale;
+  avg[1] = (double) info.ldavg[1] / info.fscale;
+  avg[2] = (double) info.ldavg[2] / info.fscale;
+}
+
+
+int uv_resident_set_memory(size_t* rss) {
+  mach_msg_type_number_t count;
+  task_basic_info_data_t info;
+  kern_return_t err;
+
+  count = TASK_BASIC_INFO_COUNT;
+  err = task_info(mach_task_self(),
+                  TASK_BASIC_INFO,
+                  (task_info_t) &info,
+                  &count);
+  (void) &err;
+  /* task_info(TASK_BASIC_INFO) cannot really fail. Anything other than
+   * KERN_SUCCESS implies a libuv bug.
+   */
+  assert(err == KERN_SUCCESS);
+  *rss = info.resident_size;
+
+  return 0;
+}
+
+
+int uv_uptime(double* uptime) {
+  time_t now;
+  struct timeval info;
+  size_t size = sizeof(info);
+  static int which[] = {CTL_KERN, KERN_BOOTTIME};
+
+  if (sysctl(which, ARRAY_SIZE(which), &info, &size, NULL, 0))
+    return UV__ERR(errno);
+
+  now = time(NULL);
+  *uptime = now - info.tv_sec;
+
+  return 0;
+}
+
+static int uv__get_cpu_speed(uint64_t* speed) {
+  /* IOKit */
+  void (*pIOObjectRelease)(io_object_t);
+  kern_return_t (*pIOMasterPort)(mach_port_t, mach_port_t*);
+  CFMutableDictionaryRef (*pIOServiceMatching)(const char*);
+  kern_return_t (*pIOServiceGetMatchingServices)(mach_port_t,
+                                                 CFMutableDictionaryRef,
+                                                 io_iterator_t*);
+  io_service_t (*pIOIteratorNext)(io_iterator_t);
+  CFTypeRef (*pIORegistryEntryCreateCFProperty)(io_registry_entry_t,
+                                                CFStringRef,
+                                                CFAllocatorRef,
+                                                IOOptionBits);
+
+  /* CoreFoundation */
+  CFStringRef (*pCFStringCreateWithCString)(CFAllocatorRef,
+                                            const char*,
+                                            CFStringEncoding);
+  CFStringEncoding (*pCFStringGetSystemEncoding)(void);
+  UInt8 *(*pCFDataGetBytePtr)(CFDataRef);
+  CFIndex (*pCFDataGetLength)(CFDataRef);
+  void (*pCFDataGetBytes)(CFDataRef, CFRange, UInt8*);
+  void (*pCFRelease)(CFTypeRef);
+
+  void* core_foundation_handle;
+  void* iokit_handle;
+  int err;
+
+  kern_return_t kr;
+  mach_port_t mach_port;
+  io_iterator_t it;
+  io_object_t service;
+
+  mach_port = 0;
+
+  err = UV_ENOENT;
+  core_foundation_handle = dlopen("/System/Library/Frameworks/"
+                                  "CoreFoundation.framework/"
+                                  "CoreFoundation",
+                                  RTLD_LAZY | RTLD_LOCAL);
+  iokit_handle = dlopen("/System/Library/Frameworks/IOKit.framework/"
+                        "IOKit",
+                        RTLD_LAZY | RTLD_LOCAL);
+
+  if (core_foundation_handle == NULL || iokit_handle == NULL)
+    goto out;
+
+#define V(handle, symbol)                                                     \
+  do {                                                                        \
+    *(void **)(&p ## symbol) = dlsym((handle), #symbol);                      \
+    if (p ## symbol == NULL)                                                  \
+      goto out;                                                               \
+  }                                                                           \
+  while (0)
+  V(iokit_handle, IOMasterPort);
+  V(iokit_handle, IOServiceMatching);
+  V(iokit_handle, IOServiceGetMatchingServices);
+  V(iokit_handle, IOIteratorNext);
+  V(iokit_handle, IOObjectRelease);
+  V(iokit_handle, IORegistryEntryCreateCFProperty);
+  V(core_foundation_handle, CFStringCreateWithCString);
+  V(core_foundation_handle, CFStringGetSystemEncoding);
+  V(core_foundation_handle, CFDataGetBytePtr);
+  V(core_foundation_handle, CFDataGetLength);
+  V(core_foundation_handle, CFDataGetBytes);
+  V(core_foundation_handle, CFRelease);
+#undef V
+
+#define S(s) pCFStringCreateWithCString(NULL, (s), kCFStringEncodingUTF8)
+
+  // Braces ensure goto doesn't jump into device_type_str's and
+  // clock_frequency_str's lifetimes after their initialization
+  {
+    kr = pIOMasterPort(MACH_PORT_NULL, &mach_port);
+    (void) kr;
+    assert(kr == KERN_SUCCESS);
+    CFMutableDictionaryRef classes_to_match
+        = pIOServiceMatching("IOPlatformDevice");
+    kr = pIOServiceGetMatchingServices(mach_port, classes_to_match, &it);
+    assert(kr == KERN_SUCCESS);
+    service = pIOIteratorNext(it);
+
+    CFStringRef device_type_str = S("device_type");
+    CFStringRef clock_frequency_str = S("clock-frequency");
+
+    while (service != 0) {
+      CFDataRef data;
+      data = pIORegistryEntryCreateCFProperty(service,
+                                              device_type_str,
+                                              NULL,
+                                              0);
+      if (data) {
+        const UInt8* raw = pCFDataGetBytePtr(data);
+        if (strncmp((char*)raw, "cpu", 3) == 0 ||
+            strncmp((char*)raw, "processor", 9) == 0) {
+          CFDataRef freq_ref;
+          freq_ref = pIORegistryEntryCreateCFProperty(service,
+                                                      clock_frequency_str,
+                                                      NULL,
+                                                      0);
+          if (freq_ref) {
+            const UInt8* freq_ref_ptr = pCFDataGetBytePtr(freq_ref);
+            CFIndex len = pCFDataGetLength(freq_ref);
+            if (len == 8)
+              memcpy(speed, freq_ref_ptr, 8);
+            else if (len == 4) {
+              uint32_t v;
+              memcpy(&v, freq_ref_ptr, 4);
+              *speed = v;
+            } else {
+              *speed = 0;
+            }
+
+            pCFRelease(freq_ref);
+            pCFRelease(data);
+            break;
+          }
+        }
+        pCFRelease(data);
+      }
+
+      service = pIOIteratorNext(it);
+    }
+
+    pIOObjectRelease(it);
+
+    err = 0;
+
+    if (device_type_str != NULL)
+      pCFRelease(device_type_str);
+    if (clock_frequency_str != NULL)
+      pCFRelease(clock_frequency_str);
+  }
+
+out:
+  if (core_foundation_handle != NULL)
+    dlclose(core_foundation_handle);
+
+  if (iokit_handle != NULL)
+    dlclose(iokit_handle);
+
+  mach_port_deallocate(mach_task_self(), mach_port);
+
+  return err;
+}
+
+int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) {
+  unsigned int ticks = (unsigned int)sysconf(_SC_CLK_TCK),
+               multiplier = ((uint64_t)1000L / ticks);
+  char model[512];
+  size_t size;
+  unsigned int i;
+  natural_t numcpus;
+  mach_msg_type_number_t msg_type;
+  processor_cpu_load_info_data_t *info;
+  uv_cpu_info_t* cpu_info;
+  uint64_t cpuspeed;
+  int err;
+
+  size = sizeof(model);
+  if (sysctlbyname("machdep.cpu.brand_string", &model, &size, NULL, 0) &&
+      sysctlbyname("hw.model", &model, &size, NULL, 0)) {
+    return UV__ERR(errno);
+  }
+
+  err = uv__get_cpu_speed(&cpuspeed);
+  if (err < 0)
+    return err;
+
+  if (host_processor_info(mach_host_self(), PROCESSOR_CPU_LOAD_INFO, &numcpus,
+                          (processor_info_array_t*)&info,
+                          &msg_type) != KERN_SUCCESS) {
+    return UV_EINVAL;  /* FIXME(bnoordhuis) Translate error. */
+  }
+
+  *cpu_infos = (uv_cpu_info_t*)uv__malloc(numcpus * sizeof(**cpu_infos));
+  if (!(*cpu_infos)) {
+    vm_deallocate(mach_task_self(), (vm_address_t)info, msg_type);
+    return UV_ENOMEM;
+  }
+
+  *count = numcpus;
+
+  for (i = 0; i < numcpus; i++) {
+    cpu_info = &(*cpu_infos)[i];
+
+    cpu_info->cpu_times.user = (uint64_t)(info[i].cpu_ticks[0]) * multiplier;
+    cpu_info->cpu_times.nice = (uint64_t)(info[i].cpu_ticks[3]) * multiplier;
+    cpu_info->cpu_times.sys = (uint64_t)(info[i].cpu_ticks[1]) * multiplier;
+    cpu_info->cpu_times.idle = (uint64_t)(info[i].cpu_ticks[2]) * multiplier;
+    cpu_info->cpu_times.irq = 0;
+
+    cpu_info->model = uv__strdup(model);
+    cpu_info->speed = cpuspeed/1000000;
+  }
+  vm_deallocate(mach_task_self(), (vm_address_t)info, msg_type);
+
+  return 0;
+}
diff --git a/wpinet/src/main/native/thirdparty/libuv/src/unix/dl.cpp b/wpinet/src/main/native/thirdparty/libuv/src/unix/dl.cpp
new file mode 100644
index 0000000..80b3333
--- /dev/null
+++ b/wpinet/src/main/native/thirdparty/libuv/src/unix/dl.cpp
@@ -0,0 +1,80 @@
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include "uv.h"
+#include "internal.h"
+
+#include <dlfcn.h>
+#include <errno.h>
+#include <string.h>
+#include <locale.h>
+
+static int uv__dlerror(uv_lib_t* lib);
+
+
+int uv_dlopen(const char* filename, uv_lib_t* lib) {
+  dlerror(); /* Reset error status. */
+  lib->errmsg = NULL;
+  lib->handle = dlopen(filename, RTLD_LAZY);
+  return lib->handle ? 0 : uv__dlerror(lib);
+}
+
+
+void uv_dlclose(uv_lib_t* lib) {
+  uv__free(lib->errmsg);
+  lib->errmsg = NULL;
+
+  if (lib->handle) {
+    /* Ignore errors. No good way to signal them without leaking memory. */
+    dlclose(lib->handle);
+    lib->handle = NULL;
+  }
+}
+
+
+int uv_dlsym(uv_lib_t* lib, const char* name, void** ptr) {
+  dlerror(); /* Reset error status. */
+  *ptr = dlsym(lib->handle, name);
+  return *ptr ? 0 : uv__dlerror(lib);
+}
+
+
+const char* uv_dlerror(const uv_lib_t* lib) {
+  return lib->errmsg ? lib->errmsg : "no error";
+}
+
+
+static int uv__dlerror(uv_lib_t* lib) {
+  const char* errmsg;
+
+  uv__free(lib->errmsg);
+
+  errmsg = dlerror();
+
+  if (errmsg) {
+    lib->errmsg = uv__strdup(errmsg);
+    return -1;
+  }
+  else {
+    lib->errmsg = NULL;
+    return 0;
+  }
+}
diff --git a/wpinet/src/main/native/thirdparty/libuv/src/unix/epoll.cpp b/wpinet/src/main/native/thirdparty/libuv/src/unix/epoll.cpp
new file mode 100644
index 0000000..4c057fb
--- /dev/null
+++ b/wpinet/src/main/native/thirdparty/libuv/src/unix/epoll.cpp
@@ -0,0 +1,422 @@
+/* Copyright libuv contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include "uv.h"
+#include "internal.h"
+#include <errno.h>
+#include <sys/epoll.h>
+
+int uv__epoll_init(uv_loop_t* loop) {
+  int fd;
+  fd = epoll_create1(O_CLOEXEC);
+
+  /* epoll_create1() can fail either because it's not implemented (old kernel)
+   * or because it doesn't understand the O_CLOEXEC flag.
+   */
+  if (fd == -1 && (errno == ENOSYS || errno == EINVAL)) {
+    fd = epoll_create(256);
+
+    if (fd != -1)
+      uv__cloexec(fd, 1);
+  }
+
+  loop->backend_fd = fd;
+  if (fd == -1)
+    return UV__ERR(errno);
+
+  return 0;
+}
+
+
+void uv__platform_invalidate_fd(uv_loop_t* loop, int fd) {
+  struct epoll_event* events;
+  struct epoll_event dummy;
+  uintptr_t i;
+  uintptr_t nfds;
+
+  assert(loop->watchers != NULL);
+  assert(fd >= 0);
+
+  events = (struct epoll_event*) loop->watchers[loop->nwatchers];
+  nfds = (uintptr_t) loop->watchers[loop->nwatchers + 1];
+  if (events != NULL)
+    /* Invalidate events with same file descriptor */
+    for (i = 0; i < nfds; i++)
+      if (events[i].data.fd == fd)
+        events[i].data.fd = -1;
+
+  /* Remove the file descriptor from the epoll.
+   * This avoids a problem where the same file description remains open
+   * in another process, causing repeated junk epoll events.
+   *
+   * We pass in a dummy epoll_event, to work around a bug in old kernels.
+   */
+  if (loop->backend_fd >= 0) {
+    /* Work around a bug in kernels 3.10 to 3.19 where passing a struct that
+     * has the EPOLLWAKEUP flag set generates spurious audit syslog warnings.
+     */
+    memset(&dummy, 0, sizeof(dummy));
+    epoll_ctl(loop->backend_fd, EPOLL_CTL_DEL, fd, &dummy);
+  }
+}
+
+
+int uv__io_check_fd(uv_loop_t* loop, int fd) {
+  struct epoll_event e;
+  int rc;
+
+  memset(&e, 0, sizeof(e));
+  e.events = POLLIN;
+  e.data.fd = -1;
+
+  rc = 0;
+  if (epoll_ctl(loop->backend_fd, EPOLL_CTL_ADD, fd, &e))
+    if (errno != EEXIST)
+      rc = UV__ERR(errno);
+
+  if (rc == 0)
+    if (epoll_ctl(loop->backend_fd, EPOLL_CTL_DEL, fd, &e))
+      abort();
+
+  return rc;
+}
+
+
+void uv__io_poll(uv_loop_t* loop, int timeout) {
+  /* A bug in kernels < 2.6.37 makes timeouts larger than ~30 minutes
+   * effectively infinite on 32 bits architectures.  To avoid blocking
+   * indefinitely, we cap the timeout and poll again if necessary.
+   *
+   * Note that "30 minutes" is a simplification because it depends on
+   * the value of CONFIG_HZ.  The magic constant assumes CONFIG_HZ=1200,
+   * that being the largest value I have seen in the wild (and only once.)
+   */
+  static const int max_safe_timeout = 1789569;
+  static int no_epoll_pwait_cached;
+  static int no_epoll_wait_cached;
+  int no_epoll_pwait;
+  int no_epoll_wait;
+  struct epoll_event events[1024];
+  struct epoll_event* pe;
+  struct epoll_event e;
+  int real_timeout;
+  QUEUE* q;
+  uv__io_t* w;
+  sigset_t sigset;
+  uint64_t sigmask;
+  uint64_t base;
+  int have_signals;
+  int nevents;
+  int count;
+  int nfds;
+  int fd;
+  int op;
+  int i;
+  int user_timeout;
+  int reset_timeout;
+
+  if (loop->nfds == 0) {
+    assert(QUEUE_EMPTY(&loop->watcher_queue));
+    return;
+  }
+
+  memset(&e, 0, sizeof(e));
+
+  while (!QUEUE_EMPTY(&loop->watcher_queue)) {
+    q = QUEUE_HEAD(&loop->watcher_queue);
+    QUEUE_REMOVE(q);
+    QUEUE_INIT(q);
+
+    w = QUEUE_DATA(q, uv__io_t, watcher_queue);
+    assert(w->pevents != 0);
+    assert(w->fd >= 0);
+    assert(w->fd < (int) loop->nwatchers);
+
+    e.events = w->pevents;
+    e.data.fd = w->fd;
+
+    if (w->events == 0)
+      op = EPOLL_CTL_ADD;
+    else
+      op = EPOLL_CTL_MOD;
+
+    /* XXX Future optimization: do EPOLL_CTL_MOD lazily if we stop watching
+     * events, skip the syscall and squelch the events after epoll_wait().
+     */
+    if (epoll_ctl(loop->backend_fd, op, w->fd, &e)) {
+      if (errno != EEXIST)
+        abort();
+
+      assert(op == EPOLL_CTL_ADD);
+
+      /* We've reactivated a file descriptor that's been watched before. */
+      if (epoll_ctl(loop->backend_fd, EPOLL_CTL_MOD, w->fd, &e))
+        abort();
+    }
+
+    w->events = w->pevents;
+  }
+
+  sigmask = 0;
+  if (loop->flags & UV_LOOP_BLOCK_SIGPROF) {
+    sigemptyset(&sigset);
+    sigaddset(&sigset, SIGPROF);
+    sigmask |= 1 << (SIGPROF - 1);
+  }
+
+  assert(timeout >= -1);
+  base = loop->time;
+  count = 48; /* Benchmarks suggest this gives the best throughput. */
+  real_timeout = timeout;
+
+  if (uv__get_internal_fields(loop)->flags & UV_METRICS_IDLE_TIME) {
+    reset_timeout = 1;
+    user_timeout = timeout;
+    timeout = 0;
+  } else {
+    reset_timeout = 0;
+    user_timeout = 0;
+  }
+
+  /* You could argue there is a dependency between these two but
+   * ultimately we don't care about their ordering with respect
+   * to one another. Worst case, we make a few system calls that
+   * could have been avoided because another thread already knows
+   * they fail with ENOSYS. Hardly the end of the world.
+   */
+  no_epoll_pwait = uv__load_relaxed(&no_epoll_pwait_cached);
+  no_epoll_wait = uv__load_relaxed(&no_epoll_wait_cached);
+
+  for (;;) {
+    /* Only need to set the provider_entry_time if timeout != 0. The function
+     * will return early if the loop isn't configured with UV_METRICS_IDLE_TIME.
+     */
+    if (timeout != 0)
+      uv__metrics_set_provider_entry_time(loop);
+
+    /* See the comment for max_safe_timeout for an explanation of why
+     * this is necessary.  Executive summary: kernel bug workaround.
+     */
+    if (sizeof(int32_t) == sizeof(long) && timeout >= max_safe_timeout)
+      timeout = max_safe_timeout;
+
+    if (sigmask != 0 && no_epoll_pwait != 0)
+      if (pthread_sigmask(SIG_BLOCK, &sigset, NULL))
+        abort();
+
+    if (no_epoll_wait != 0 || (sigmask != 0 && no_epoll_pwait == 0)) {
+      nfds = epoll_pwait(loop->backend_fd,
+                         events,
+                         ARRAY_SIZE(events),
+                         timeout,
+                         &sigset);
+      if (nfds == -1 && errno == ENOSYS) {
+        uv__store_relaxed(&no_epoll_pwait_cached, 1);
+        no_epoll_pwait = 1;
+      }
+    } else {
+      nfds = epoll_wait(loop->backend_fd,
+                        events,
+                        ARRAY_SIZE(events),
+                        timeout);
+      if (nfds == -1 && errno == ENOSYS) {
+        uv__store_relaxed(&no_epoll_wait_cached, 1);
+        no_epoll_wait = 1;
+      }
+    }
+
+    if (sigmask != 0 && no_epoll_pwait != 0)
+      if (pthread_sigmask(SIG_UNBLOCK, &sigset, NULL))
+        abort();
+
+    /* Update loop->time unconditionally. It's tempting to skip the update when
+     * timeout == 0 (i.e. non-blocking poll) but there is no guarantee that the
+     * operating system didn't reschedule our process while in the syscall.
+     */
+    SAVE_ERRNO(uv__update_time(loop));
+
+    if (nfds == 0) {
+      assert(timeout != -1);
+
+      if (reset_timeout != 0) {
+        timeout = user_timeout;
+        reset_timeout = 0;
+      }
+
+      if (timeout == -1)
+        continue;
+
+      if (timeout == 0)
+        return;
+
+      /* We may have been inside the system call for longer than |timeout|
+       * milliseconds so we need to update the timestamp to avoid drift.
+       */
+      goto update_timeout;
+    }
+
+    if (nfds == -1) {
+      if (errno == ENOSYS) {
+        /* epoll_wait() or epoll_pwait() failed, try the other system call. */
+        assert(no_epoll_wait == 0 || no_epoll_pwait == 0);
+        continue;
+      }
+
+      if (errno != EINTR)
+        abort();
+
+      if (reset_timeout != 0) {
+        timeout = user_timeout;
+        reset_timeout = 0;
+      }
+
+      if (timeout == -1)
+        continue;
+
+      if (timeout == 0)
+        return;
+
+      /* Interrupted by a signal. Update timeout and poll again. */
+      goto update_timeout;
+    }
+
+    have_signals = 0;
+    nevents = 0;
+
+    {
+      /* Squelch a -Waddress-of-packed-member warning with gcc >= 9. */
+      union {
+        struct epoll_event* events;
+        uv__io_t* watchers;
+      } x;
+
+      x.events = events;
+      assert(loop->watchers != NULL);
+      loop->watchers[loop->nwatchers] = x.watchers;
+      loop->watchers[loop->nwatchers + 1] = (void*) (uintptr_t) nfds;
+    }
+
+    for (i = 0; i < nfds; i++) {
+      pe = events + i;
+      fd = pe->data.fd;
+
+      /* Skip invalidated events, see uv__platform_invalidate_fd */
+      if (fd == -1)
+        continue;
+
+      assert(fd >= 0);
+      assert((unsigned) fd < loop->nwatchers);
+
+      w = (uv__io_t*)loop->watchers[fd];
+
+      if (w == NULL) {
+        /* File descriptor that we've stopped watching, disarm it.
+         *
+         * Ignore all errors because we may be racing with another thread
+         * when the file descriptor is closed.
+         */
+        epoll_ctl(loop->backend_fd, EPOLL_CTL_DEL, fd, pe);
+        continue;
+      }
+
+      /* Give users only events they're interested in. Prevents spurious
+       * callbacks when previous callback invocation in this loop has stopped
+       * the current watcher. Also, filters out events that users has not
+       * requested us to watch.
+       */
+      pe->events &= w->pevents | POLLERR | POLLHUP;
+
+      /* Work around an epoll quirk where it sometimes reports just the
+       * EPOLLERR or EPOLLHUP event.  In order to force the event loop to
+       * move forward, we merge in the read/write events that the watcher
+       * is interested in; uv__read() and uv__write() will then deal with
+       * the error or hangup in the usual fashion.
+       *
+       * Note to self: happens when epoll reports EPOLLIN|EPOLLHUP, the user
+       * reads the available data, calls uv_read_stop(), then sometime later
+       * calls uv_read_start() again.  By then, libuv has forgotten about the
+       * hangup and the kernel won't report EPOLLIN again because there's
+       * nothing left to read.  If anything, libuv is to blame here.  The
+       * current hack is just a quick bandaid; to properly fix it, libuv
+       * needs to remember the error/hangup event.  We should get that for
+       * free when we switch over to edge-triggered I/O.
+       */
+      if (pe->events == POLLERR || pe->events == POLLHUP)
+        pe->events |=
+          w->pevents & (POLLIN | POLLOUT | UV__POLLRDHUP | UV__POLLPRI);
+
+      if (pe->events != 0) {
+        /* Run signal watchers last.  This also affects child process watchers
+         * because those are implemented in terms of signal watchers.
+         */
+        if (w == &loop->signal_io_watcher) {
+          have_signals = 1;
+        } else {
+          uv__metrics_update_idle_time(loop);
+          w->cb(loop, w, pe->events);
+        }
+
+        nevents++;
+      }
+    }
+
+    if (reset_timeout != 0) {
+      timeout = user_timeout;
+      reset_timeout = 0;
+    }
+
+    if (have_signals != 0) {
+      uv__metrics_update_idle_time(loop);
+      loop->signal_io_watcher.cb(loop, &loop->signal_io_watcher, POLLIN);
+    }
+
+    loop->watchers[loop->nwatchers] = NULL;
+    loop->watchers[loop->nwatchers + 1] = NULL;
+
+    if (have_signals != 0)
+      return;  /* Event loop should cycle now so don't poll again. */
+
+    if (nevents != 0) {
+      if (nfds == ARRAY_SIZE(events) && --count != 0) {
+        /* Poll for more events but don't block this time. */
+        timeout = 0;
+        continue;
+      }
+      return;
+    }
+
+    if (timeout == 0)
+      return;
+
+    if (timeout == -1)
+      continue;
+
+update_timeout:
+    assert(timeout > 0);
+
+    real_timeout -= (loop->time - base);
+    if (real_timeout <= 0)
+      return;
+
+    timeout = real_timeout;
+  }
+}
+
diff --git a/wpinet/src/main/native/thirdparty/libuv/src/unix/freebsd.cpp b/wpinet/src/main/native/thirdparty/libuv/src/unix/freebsd.cpp
new file mode 100644
index 0000000..6700ff6
--- /dev/null
+++ b/wpinet/src/main/native/thirdparty/libuv/src/unix/freebsd.cpp
@@ -0,0 +1,304 @@
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include "uv.h"
+#include "internal.h"
+
+#include <assert.h>
+#include <string.h>
+#include <errno.h>
+
+#include <paths.h>
+#include <sys/user.h>
+#include <sys/types.h>
+#include <sys/resource.h>
+#include <sys/sysctl.h>
+#include <vm/vm_param.h> /* VM_LOADAVG */
+#include <time.h>
+#include <stdlib.h>
+#include <unistd.h> /* sysconf */
+#include <fcntl.h>
+
+#ifndef CPUSTATES
+# define CPUSTATES 5U
+#endif
+#ifndef CP_USER
+# define CP_USER 0
+# define CP_NICE 1
+# define CP_SYS 2
+# define CP_IDLE 3
+# define CP_INTR 4
+#endif
+
+
+int uv__platform_loop_init(uv_loop_t* loop) {
+  return uv__kqueue_init(loop);
+}
+
+
+void uv__platform_loop_delete(uv_loop_t* loop) {
+}
+
+int uv_exepath(char* buffer, size_t* size) {
+  char abspath[PATH_MAX * 2 + 1];
+  int mib[4];
+  size_t abspath_size;
+
+  if (buffer == NULL || size == NULL || *size == 0)
+    return UV_EINVAL;
+
+  mib[0] = CTL_KERN;
+  mib[1] = KERN_PROC;
+  mib[2] = KERN_PROC_PATHNAME;
+  mib[3] = -1;
+
+  abspath_size = sizeof abspath;
+  if (sysctl(mib, ARRAY_SIZE(mib), abspath, &abspath_size, NULL, 0))
+    return UV__ERR(errno);
+
+  assert(abspath_size > 0);
+  abspath_size -= 1;
+  *size -= 1;
+
+  if (*size > abspath_size)
+    *size = abspath_size;
+
+  memcpy(buffer, abspath, *size);
+  buffer[*size] = '\0';
+
+  return 0;
+}
+
+uint64_t uv_get_free_memory(void) {
+  int freecount;
+  size_t size = sizeof(freecount);
+
+  if (sysctlbyname("vm.stats.vm.v_free_count", &freecount, &size, NULL, 0))
+    return UV__ERR(errno);
+
+  return (uint64_t) freecount * sysconf(_SC_PAGESIZE);
+
+}
+
+
+uint64_t uv_get_total_memory(void) {
+  unsigned long info;
+  int which[] = {CTL_HW, HW_PHYSMEM};
+
+  size_t size = sizeof(info);
+
+  if (sysctl(which, ARRAY_SIZE(which), &info, &size, NULL, 0))
+    return UV__ERR(errno);
+
+  return (uint64_t) info;
+}
+
+
+uint64_t uv_get_constrained_memory(void) {
+  return 0;  /* Memory constraints are unknown. */
+}
+
+
+void uv_loadavg(double avg[3]) {
+  struct loadavg info;
+  size_t size = sizeof(info);
+  int which[] = {CTL_VM, VM_LOADAVG};
+
+  if (sysctl(which, ARRAY_SIZE(which), &info, &size, NULL, 0) < 0) return;
+
+  avg[0] = (double) info.ldavg[0] / info.fscale;
+  avg[1] = (double) info.ldavg[1] / info.fscale;
+  avg[2] = (double) info.ldavg[2] / info.fscale;
+}
+
+
+int uv_resident_set_memory(size_t* rss) {
+  struct kinfo_proc kinfo;
+  size_t page_size;
+  size_t kinfo_size;
+  int mib[4];
+
+  mib[0] = CTL_KERN;
+  mib[1] = KERN_PROC;
+  mib[2] = KERN_PROC_PID;
+  mib[3] = getpid();
+
+  kinfo_size = sizeof(kinfo);
+
+  if (sysctl(mib, ARRAY_SIZE(mib), &kinfo, &kinfo_size, NULL, 0))
+    return UV__ERR(errno);
+
+  page_size = getpagesize();
+
+#ifdef __DragonFly__
+  *rss = kinfo.kp_vm_rssize * page_size;
+#else
+  *rss = kinfo.ki_rssize * page_size;
+#endif
+
+  return 0;
+}
+
+
+int uv_uptime(double* uptime) {
+  int r;
+  struct timespec sp;
+  r = clock_gettime(CLOCK_MONOTONIC, &sp);
+  if (r)
+    return UV__ERR(errno);
+
+  *uptime = sp.tv_sec;
+  return 0;
+}
+
+
+int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) {
+  unsigned int ticks = (unsigned int)sysconf(_SC_CLK_TCK),
+               multiplier = ((uint64_t)1000L / ticks), cpuspeed, maxcpus,
+               cur = 0;
+  uv_cpu_info_t* cpu_info;
+  const char* maxcpus_key;
+  const char* cptimes_key;
+  const char* model_key;
+  char model[512];
+  long* cp_times;
+  int numcpus;
+  size_t size;
+  int i;
+
+#if defined(__DragonFly__)
+  /* This is not quite correct but DragonFlyBSD doesn't seem to have anything
+   * comparable to kern.smp.maxcpus or kern.cp_times (kern.cp_time is a total,
+   * not per CPU). At least this stops uv_cpu_info() from failing completely.
+   */
+  maxcpus_key = "hw.ncpu";
+  cptimes_key = "kern.cp_time";
+#else
+  maxcpus_key = "kern.smp.maxcpus";
+  cptimes_key = "kern.cp_times";
+#endif
+
+#if defined(__arm__) || defined(__aarch64__)
+  /* The key hw.model and hw.clockrate are not available on FreeBSD ARM. */
+  model_key = "hw.machine";
+  cpuspeed = 0;
+#else
+  model_key = "hw.model";
+
+  size = sizeof(cpuspeed);
+  if (sysctlbyname("hw.clockrate", &cpuspeed, &size, NULL, 0))
+    return -errno;
+#endif
+
+  size = sizeof(model);
+  if (sysctlbyname(model_key, &model, &size, NULL, 0))
+    return UV__ERR(errno);
+
+  size = sizeof(numcpus);
+  if (sysctlbyname("hw.ncpu", &numcpus, &size, NULL, 0))
+    return UV__ERR(errno);
+
+  *cpu_infos = (uv_cpu_info_t*)uv__malloc(numcpus * sizeof(**cpu_infos));
+  if (!(*cpu_infos))
+    return UV_ENOMEM;
+
+  *count = numcpus;
+
+  /* kern.cp_times on FreeBSD i386 gives an array up to maxcpus instead of
+   * ncpu.
+   */
+  size = sizeof(maxcpus);
+  if (sysctlbyname(maxcpus_key, &maxcpus, &size, NULL, 0)) {
+    uv__free(*cpu_infos);
+    return UV__ERR(errno);
+  }
+
+  size = maxcpus * CPUSTATES * sizeof(long);
+
+  cp_times = (long*)uv__malloc(size);
+  if (cp_times == NULL) {
+    uv__free(*cpu_infos);
+    return UV_ENOMEM;
+  }
+
+  if (sysctlbyname(cptimes_key, cp_times, &size, NULL, 0)) {
+    uv__free(cp_times);
+    uv__free(*cpu_infos);
+    return UV__ERR(errno);
+  }
+
+  for (i = 0; i < numcpus; i++) {
+    cpu_info = &(*cpu_infos)[i];
+
+    cpu_info->cpu_times.user = (uint64_t)(cp_times[CP_USER+cur]) * multiplier;
+    cpu_info->cpu_times.nice = (uint64_t)(cp_times[CP_NICE+cur]) * multiplier;
+    cpu_info->cpu_times.sys = (uint64_t)(cp_times[CP_SYS+cur]) * multiplier;
+    cpu_info->cpu_times.idle = (uint64_t)(cp_times[CP_IDLE+cur]) * multiplier;
+    cpu_info->cpu_times.irq = (uint64_t)(cp_times[CP_INTR+cur]) * multiplier;
+
+    cpu_info->model = uv__strdup(model);
+    cpu_info->speed = cpuspeed;
+
+    cur+=CPUSTATES;
+  }
+
+  uv__free(cp_times);
+  return 0;
+}
+
+
+int uv__sendmmsg(int fd, struct uv__mmsghdr* mmsg, unsigned int vlen) {
+#if __FreeBSD__ >= 11 && !defined(__DragonFly__)
+  return sendmmsg(fd,
+                  (struct mmsghdr*) mmsg,
+                  vlen,
+                  0 /* flags */);
+#else
+  return errno = ENOSYS, -1;
+#endif
+}
+
+
+int uv__recvmmsg(int fd, struct uv__mmsghdr* mmsg, unsigned int vlen) {
+#if __FreeBSD__ >= 11 && !defined(__DragonFly__)
+  return recvmmsg(fd,
+                  (struct mmsghdr*) mmsg,
+                  vlen,
+                  0 /* flags */,
+                  NULL /* timeout */);
+#else
+  return errno = ENOSYS, -1;
+#endif
+}
+
+ssize_t
+uv__fs_copy_file_range(int fd_in,
+                       off_t* off_in,
+                       int fd_out,
+                       off_t* off_out,
+                       size_t len,
+                       unsigned int flags)
+{
+#if __FreeBSD__ >= 13 && !defined(__DragonFly__)
+	return copy_file_range(fd_in, off_in, fd_out, off_out, len, flags);
+#else
+	return errno = ENOSYS, -1;
+#endif
+}
diff --git a/wpinet/src/main/native/thirdparty/libuv/src/unix/fs.cpp b/wpinet/src/main/native/thirdparty/libuv/src/unix/fs.cpp
new file mode 100644
index 0000000..1a61524
--- /dev/null
+++ b/wpinet/src/main/native/thirdparty/libuv/src/unix/fs.cpp
@@ -0,0 +1,2259 @@
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+/* Caveat emptor: this file deviates from the libuv convention of returning
+ * negated errno codes. Most uv_fs_*() functions map directly to the system
+ * call of the same name. For more complex wrappers, it's easier to just
+ * return -1 with errno set. The dispatcher in uv__fs_work() takes care of
+ * getting the errno to the right place (req->result or as the return value.)
+ */
+
+#include "uv.h"
+#include "internal.h"
+
+#include <errno.h>
+#include <dlfcn.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h> /* PATH_MAX */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/uio.h>
+#include <pthread.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <poll.h>
+
+#if defined(__DragonFly__)        ||                                      \
+    defined(__FreeBSD__)          ||                                      \
+    defined(__FreeBSD_kernel__)   ||                                      \
+    defined(__OpenBSD__)          ||                                      \
+    defined(__NetBSD__)
+# define HAVE_PREADV 1
+#else
+# define HAVE_PREADV 0
+#endif
+
+#if defined(__linux__)
+# include "sys/utsname.h"
+#endif
+
+#if defined(__linux__) || defined(__sun)
+# include <sys/sendfile.h>
+# include <sys/sysmacros.h>
+#endif
+
+#if defined(__APPLE__)
+# include <sys/sysctl.h>
+#elif defined(__linux__) && !defined(FICLONE)
+# include <sys/ioctl.h>
+# define FICLONE _IOW(0x94, 9, int)
+#endif
+
+#if defined(_AIX) && !defined(_AIX71)
+# include <utime.h>
+#endif
+
+#if defined(__APPLE__)            ||                                      \
+    defined(__DragonFly__)        ||                                      \
+    defined(__FreeBSD__)          ||                                      \
+    defined(__FreeBSD_kernel__)   ||                                      \
+    defined(__OpenBSD__)          ||                                      \
+    defined(__NetBSD__)
+# include <sys/param.h>
+# include <sys/mount.h>
+#elif defined(__sun)      || \
+      defined(__MVS__)    || \
+      defined(__NetBSD__) || \
+      defined(__HAIKU__)  || \
+      defined(__QNX__)
+# include <sys/statvfs.h>
+#else
+# include <sys/statfs.h>
+#endif
+
+#if defined(_AIX) && _XOPEN_SOURCE <= 600
+extern char *mkdtemp(char *template); /* See issue #740 on AIX < 7 */
+#endif
+
+#define INIT(subtype)                                                         \
+  do {                                                                        \
+    if (req == NULL)                                                          \
+      return UV_EINVAL;                                                       \
+    UV_REQ_INIT(req, UV_FS);                                                  \
+    req->fs_type = UV_FS_ ## subtype;                                         \
+    req->result = 0;                                                          \
+    req->ptr = NULL;                                                          \
+    req->loop = loop;                                                         \
+    req->path = NULL;                                                         \
+    req->new_path = NULL;                                                     \
+    req->bufs = NULL;                                                         \
+    req->cb = cb;                                                             \
+  }                                                                           \
+  while (0)
+
+#define PATH                                                                  \
+  do {                                                                        \
+    assert(path != NULL);                                                     \
+    if (cb == NULL) {                                                         \
+      req->path = path;                                                       \
+    } else {                                                                  \
+      req->path = uv__strdup(path);                                           \
+      if (req->path == NULL)                                                  \
+        return UV_ENOMEM;                                                     \
+    }                                                                         \
+  }                                                                           \
+  while (0)
+
+#define PATH2                                                                 \
+  do {                                                                        \
+    if (cb == NULL) {                                                         \
+      req->path = path;                                                       \
+      req->new_path = new_path;                                               \
+    } else {                                                                  \
+      size_t path_len;                                                        \
+      size_t new_path_len;                                                    \
+      path_len = strlen(path) + 1;                                            \
+      new_path_len = strlen(new_path) + 1;                                    \
+      req->path = (char*)uv__malloc(path_len + new_path_len);                 \
+      if (req->path == NULL)                                                  \
+        return UV_ENOMEM;                                                     \
+      req->new_path = req->path + path_len;                                   \
+      memcpy((void*) req->path, path, path_len);                              \
+      memcpy((void*) req->new_path, new_path, new_path_len);                  \
+    }                                                                         \
+  }                                                                           \
+  while (0)
+
+#define POST                                                                  \
+  do {                                                                        \
+    if (cb != NULL) {                                                         \
+      uv__req_register(loop, req);                                            \
+      uv__work_submit(loop,                                                   \
+                      &req->work_req,                                         \
+                      UV__WORK_FAST_IO,                                       \
+                      uv__fs_work,                                            \
+                      uv__fs_done);                                           \
+      return 0;                                                               \
+    }                                                                         \
+    else {                                                                    \
+      uv__fs_work(&req->work_req);                                            \
+      return req->result;                                                     \
+    }                                                                         \
+  }                                                                           \
+  while (0)
+
+
+static int uv__fs_close(int fd) {
+  int rc;
+
+  rc = uv__close_nocancel(fd);
+  if (rc == -1)
+    if (errno == EINTR || errno == EINPROGRESS)
+      rc = 0;  /* The close is in progress, not an error. */
+
+  return rc;
+}
+
+
+static ssize_t uv__fs_fsync(uv_fs_t* req) {
+#if defined(__APPLE__)
+  /* Apple's fdatasync and fsync explicitly do NOT flush the drive write cache
+   * to the drive platters. This is in contrast to Linux's fdatasync and fsync
+   * which do, according to recent man pages. F_FULLFSYNC is Apple's equivalent
+   * for flushing buffered data to permanent storage. If F_FULLFSYNC is not
+   * supported by the file system we fall back to F_BARRIERFSYNC or fsync().
+   * This is the same approach taken by sqlite, except sqlite does not issue
+   * an F_BARRIERFSYNC call.
+   */
+  int r;
+
+  r = fcntl(req->file, F_FULLFSYNC);
+  if (r != 0)
+    r = fcntl(req->file, 85 /* F_BARRIERFSYNC */);  /* fsync + barrier */
+  if (r != 0)
+    r = fsync(req->file);
+  return r;
+#else
+  return fsync(req->file);
+#endif
+}
+
+
+static ssize_t uv__fs_fdatasync(uv_fs_t* req) {
+#if defined(__linux__) || defined(__sun) || defined(__NetBSD__)
+  return fdatasync(req->file);
+#elif defined(__APPLE__)
+  /* See the comment in uv__fs_fsync. */
+  return uv__fs_fsync(req);
+#else
+  return fsync(req->file);
+#endif
+}
+
+
+UV_UNUSED(static struct timespec uv__fs_to_timespec(double time)) {
+  struct timespec ts;
+  ts.tv_sec  = time;
+  ts.tv_nsec = (time - ts.tv_sec) * 1e9;
+
+ /* TODO(bnoordhuis) Remove this. utimesat() has nanosecond resolution but we
+  * stick to microsecond resolution for the sake of consistency with other
+  * platforms. I'm the original author of this compatibility hack but I'm
+  * less convinced it's useful nowadays.
+  */
+  ts.tv_nsec -= ts.tv_nsec % 1000;
+
+  if (ts.tv_nsec < 0) {
+    ts.tv_nsec += 1e9;
+    ts.tv_sec -= 1;
+  }
+  return ts;
+}
+
+UV_UNUSED(static struct timeval uv__fs_to_timeval(double time)) {
+  struct timeval tv;
+  tv.tv_sec  = time;
+  tv.tv_usec = (time - tv.tv_sec) * 1e6;
+  if (tv.tv_usec < 0) {
+    tv.tv_usec += 1e6;
+    tv.tv_sec -= 1;
+  }
+  return tv;
+}
+
+static ssize_t uv__fs_futime(uv_fs_t* req) {
+#if defined(__linux__)                                                        \
+    || defined(_AIX71)                                                        \
+    || defined(__HAIKU__)                                                     \
+    || defined(__GNU__)
+  struct timespec ts[2];
+  ts[0] = uv__fs_to_timespec(req->atime);
+  ts[1] = uv__fs_to_timespec(req->mtime);
+  return futimens(req->file, ts);
+#elif defined(__APPLE__)                                                      \
+    || defined(__DragonFly__)                                                 \
+    || defined(__FreeBSD__)                                                   \
+    || defined(__FreeBSD_kernel__)                                            \
+    || defined(__NetBSD__)                                                    \
+    || defined(__OpenBSD__)                                                   \
+    || defined(__sun)
+  struct timeval tv[2];
+  tv[0] = uv__fs_to_timeval(req->atime);
+  tv[1] = uv__fs_to_timeval(req->mtime);
+# if defined(__sun)
+  return futimesat(req->file, NULL, tv);
+# else
+  return futimes(req->file, tv);
+# endif
+#elif defined(__MVS__)
+  attrib_t atr;
+  memset(&atr, 0, sizeof(atr));
+  atr.att_mtimechg = 1;
+  atr.att_atimechg = 1;
+  atr.att_mtime = req->mtime;
+  atr.att_atime = req->atime;
+  return __fchattr(req->file, &atr, sizeof(atr));
+#else
+  errno = ENOSYS;
+  return -1;
+#endif
+}
+
+
+static ssize_t uv__fs_mkdtemp(uv_fs_t* req) {
+  return mkdtemp((char*) req->path) ? 0 : -1;
+}
+
+
+static int (*uv__mkostemp)(char*, int);
+
+
+static void uv__mkostemp_initonce(void) {
+  /* z/os doesn't have RTLD_DEFAULT but that's okay
+   * because it doesn't have mkostemp(O_CLOEXEC) either.
+   */
+#ifdef RTLD_DEFAULT
+  uv__mkostemp = (int (*)(char*, int)) dlsym(RTLD_DEFAULT, "mkostemp");
+
+  /* We don't care about errors, but we do want to clean them up.
+   * If there has been no error, then dlerror() will just return
+   * NULL.
+   */
+  dlerror();
+#endif  /* RTLD_DEFAULT */
+}
+
+
+static int uv__fs_mkstemp(uv_fs_t* req) {
+  static uv_once_t once = UV_ONCE_INIT;
+  int r;
+#ifdef O_CLOEXEC
+  static int no_cloexec_support;
+#endif
+  static const char pattern[] = "XXXXXX";
+  static const size_t pattern_size = sizeof(pattern) - 1;
+  char* path;
+  size_t path_length;
+
+  path = (char*) req->path;
+  path_length = strlen(path);
+
+  /* EINVAL can be returned for 2 reasons:
+      1. The template's last 6 characters were not XXXXXX
+      2. open() didn't support O_CLOEXEC
+     We want to avoid going to the fallback path in case
+     of 1, so it's manually checked before. */
+  if (path_length < pattern_size ||
+      strcmp(path + path_length - pattern_size, pattern)) {
+    errno = EINVAL;
+    r = -1;
+    goto clobber;
+  }
+
+  uv_once(&once, uv__mkostemp_initonce);
+
+#ifdef O_CLOEXEC
+  if (uv__load_relaxed(&no_cloexec_support) == 0 && uv__mkostemp != NULL) {
+    r = uv__mkostemp(path, O_CLOEXEC);
+
+    if (r >= 0)
+      return r;
+
+    /* If mkostemp() returns EINVAL, it means the kernel doesn't
+       support O_CLOEXEC, so we just fallback to mkstemp() below. */
+    if (errno != EINVAL)
+      goto clobber;
+
+    /* We set the static variable so that next calls don't even
+       try to use mkostemp. */
+    uv__store_relaxed(&no_cloexec_support, 1);
+  }
+#endif  /* O_CLOEXEC */
+
+  if (req->cb != NULL)
+    uv_rwlock_rdlock(&req->loop->cloexec_lock);
+
+  r = mkstemp(path);
+
+  /* In case of failure `uv__cloexec` will leave error in `errno`,
+   * so it is enough to just set `r` to `-1`.
+   */
+  if (r >= 0 && uv__cloexec(r, 1) != 0) {
+    r = uv__close(r);
+    if (r != 0)
+      abort();
+    r = -1;
+  }
+
+  if (req->cb != NULL)
+    uv_rwlock_rdunlock(&req->loop->cloexec_lock);
+
+clobber:
+  if (r < 0)
+    path[0] = '\0';
+  return r;
+}
+
+
+static ssize_t uv__fs_open(uv_fs_t* req) {
+#ifdef O_CLOEXEC
+  return open(req->path, req->flags | O_CLOEXEC, req->mode);
+#else  /* O_CLOEXEC */
+  int r;
+
+  if (req->cb != NULL)
+    uv_rwlock_rdlock(&req->loop->cloexec_lock);
+
+  r = open(req->path, req->flags, req->mode);
+
+  /* In case of failure `uv__cloexec` will leave error in `errno`,
+   * so it is enough to just set `r` to `-1`.
+   */
+  if (r >= 0 && uv__cloexec(r, 1) != 0) {
+    r = uv__close(r);
+    if (r != 0)
+      abort();
+    r = -1;
+  }
+
+  if (req->cb != NULL)
+    uv_rwlock_rdunlock(&req->loop->cloexec_lock);
+
+  return r;
+#endif  /* O_CLOEXEC */
+}
+
+
+#if !HAVE_PREADV
+static ssize_t uv__fs_preadv(uv_file fd,
+                             uv_buf_t* bufs,
+                             unsigned int nbufs,
+                             off_t off) {
+  uv_buf_t* buf;
+  uv_buf_t* end;
+  ssize_t result;
+  ssize_t rc;
+  size_t pos;
+
+  assert(nbufs > 0);
+
+  result = 0;
+  pos = 0;
+  buf = bufs + 0;
+  end = bufs + nbufs;
+
+  for (;;) {
+    do
+      rc = pread(fd, buf->base + pos, buf->len - pos, off + result);
+    while (rc == -1 && errno == EINTR);
+
+    if (rc == 0)
+      break;
+
+    if (rc == -1 && result == 0)
+      return UV__ERR(errno);
+
+    if (rc == -1)
+      break;  /* We read some data so return that, ignore the error. */
+
+    pos += rc;
+    result += rc;
+
+    if (pos < buf->len)
+      continue;
+
+    pos = 0;
+    buf += 1;
+
+    if (buf == end)
+      break;
+  }
+
+  return result;
+}
+#endif
+
+
+static ssize_t uv__fs_read(uv_fs_t* req) {
+#if defined(__linux__)
+  static int no_preadv;
+#endif
+  unsigned int iovmax;
+  ssize_t result;
+
+  iovmax = uv__getiovmax();
+  if (req->nbufs > iovmax)
+    req->nbufs = iovmax;
+
+  if (req->off < 0) {
+    if (req->nbufs == 1)
+      result = read(req->file, req->bufs[0].base, req->bufs[0].len);
+    else
+      result = readv(req->file, (struct iovec*) req->bufs, req->nbufs);
+  } else {
+    if (req->nbufs == 1) {
+      result = pread(req->file, req->bufs[0].base, req->bufs[0].len, req->off);
+      goto done;
+    }
+
+#if HAVE_PREADV
+    result = preadv(req->file, (struct iovec*) req->bufs, req->nbufs, req->off);
+#else
+# if defined(__linux__)
+    if (uv__load_relaxed(&no_preadv)) retry:
+# endif
+    {
+      result = uv__fs_preadv(req->file, req->bufs, req->nbufs, req->off);
+    }
+# if defined(__linux__)
+    else {
+      result = uv__preadv(req->file,
+                          (struct iovec*)req->bufs,
+                          req->nbufs,
+                          req->off);
+      if (result == -1 && errno == ENOSYS) {
+        uv__store_relaxed(&no_preadv, 1);
+        goto retry;
+      }
+    }
+# endif
+#endif
+  }
+
+done:
+  /* Early cleanup of bufs allocation, since we're done with it. */
+  if (req->bufs != req->bufsml)
+    uv__free(req->bufs);
+
+  req->bufs = NULL;
+  req->nbufs = 0;
+
+#ifdef __PASE__
+  /* PASE returns EOPNOTSUPP when reading a directory, convert to EISDIR */
+  if (result == -1 && errno == EOPNOTSUPP) {
+    struct stat buf;
+    ssize_t rc;
+    rc = fstat(req->file, &buf);
+    if (rc == 0 && S_ISDIR(buf.st_mode)) {
+      errno = EISDIR;
+    }
+  }
+#endif
+
+  return result;
+}
+
+
+#if defined(__APPLE__) && !defined(MAC_OS_X_VERSION_10_8)
+#define UV_CONST_DIRENT uv__dirent_t
+#else
+#define UV_CONST_DIRENT const uv__dirent_t
+#endif
+
+
+static int uv__fs_scandir_filter(UV_CONST_DIRENT* dent) {
+  return strcmp(dent->d_name, ".") != 0 && strcmp(dent->d_name, "..") != 0;
+}
+
+
+static int uv__fs_scandir_sort(UV_CONST_DIRENT** a, UV_CONST_DIRENT** b) {
+  return strcmp((*a)->d_name, (*b)->d_name);
+}
+
+
+static ssize_t uv__fs_scandir(uv_fs_t* req) {
+  uv__dirent_t** dents;
+  int n;
+
+  dents = NULL;
+  n = scandir(req->path, &dents, uv__fs_scandir_filter, uv__fs_scandir_sort);
+
+  /* NOTE: We will use nbufs as an index field */
+  req->nbufs = 0;
+
+  if (n == 0) {
+    /* OS X still needs to deallocate some memory.
+     * Memory was allocated using the system allocator, so use free() here.
+     */
+    free(dents);
+    dents = NULL;
+  } else if (n == -1) {
+    return n;
+  }
+
+  req->ptr = dents;
+
+  return n;
+}
+
+static int uv__fs_opendir(uv_fs_t* req) {
+  uv_dir_t* dir;
+
+  dir = (uv_dir_t*)uv__malloc(sizeof(*dir));
+  if (dir == NULL)
+    goto error;
+
+  dir->dir = opendir(req->path);
+  if (dir->dir == NULL)
+    goto error;
+
+  req->ptr = dir;
+  return 0;
+
+error:
+  uv__free(dir);
+  req->ptr = NULL;
+  return -1;
+}
+
+static int uv__fs_readdir(uv_fs_t* req) {
+  uv_dir_t* dir;
+  uv_dirent_t* dirent;
+  struct dirent* res;
+  unsigned int dirent_idx;
+  unsigned int i;
+
+  dir = (uv_dir_t*)req->ptr;
+  dirent_idx = 0;
+
+  while (dirent_idx < dir->nentries) {
+    /* readdir() returns NULL on end of directory, as well as on error. errno
+       is used to differentiate between the two conditions. */
+    errno = 0;
+    res = readdir(dir->dir);
+
+    if (res == NULL) {
+      if (errno != 0)
+        goto error;
+      break;
+    }
+
+    if (strcmp(res->d_name, ".") == 0 || strcmp(res->d_name, "..") == 0)
+      continue;
+
+    dirent = &dir->dirents[dirent_idx];
+    dirent->name = uv__strdup(res->d_name);
+
+    if (dirent->name == NULL)
+      goto error;
+
+    dirent->type = uv__fs_get_dirent_type(res);
+    ++dirent_idx;
+  }
+
+  return dirent_idx;
+
+error:
+  for (i = 0; i < dirent_idx; ++i) {
+    uv__free((char*) dir->dirents[i].name);
+    dir->dirents[i].name = NULL;
+  }
+
+  return -1;
+}
+
+static int uv__fs_closedir(uv_fs_t* req) {
+  uv_dir_t* dir;
+
+  dir = (uv_dir_t*)req->ptr;
+
+  if (dir->dir != NULL) {
+    closedir(dir->dir);
+    dir->dir = NULL;
+  }
+
+  uv__free(req->ptr);
+  req->ptr = NULL;
+  return 0;
+}
+
+static int uv__fs_statfs(uv_fs_t* req) {
+  uv_statfs_t* stat_fs;
+#if defined(__sun)      || \
+    defined(__MVS__)    || \
+    defined(__NetBSD__) || \
+    defined(__HAIKU__)  || \
+    defined(__QNX__)
+  struct statvfs buf;
+
+  if (0 != statvfs(req->path, &buf))
+#else
+  struct statfs buf;
+
+  if (0 != statfs(req->path, &buf))
+#endif /* defined(__sun) */
+    return -1;
+
+  stat_fs = (uv_statfs_t*)uv__malloc(sizeof(*stat_fs));
+  if (stat_fs == NULL) {
+    errno = ENOMEM;
+    return -1;
+  }
+
+#if defined(__sun)        || \
+    defined(__MVS__)      || \
+    defined(__OpenBSD__)  || \
+    defined(__NetBSD__)   || \
+    defined(__HAIKU__)    || \
+    defined(__QNX__)
+  stat_fs->f_type = 0;  /* f_type is not supported. */
+#else
+  stat_fs->f_type = buf.f_type;
+#endif
+  stat_fs->f_bsize = buf.f_bsize;
+  stat_fs->f_blocks = buf.f_blocks;
+  stat_fs->f_bfree = buf.f_bfree;
+  stat_fs->f_bavail = buf.f_bavail;
+  stat_fs->f_files = buf.f_files;
+  stat_fs->f_ffree = buf.f_ffree;
+  req->ptr = stat_fs;
+  return 0;
+}
+
+static ssize_t uv__fs_pathmax_size(const char* path) {
+  ssize_t pathmax;
+
+  pathmax = pathconf(path, _PC_PATH_MAX);
+
+  if (pathmax == -1)
+    pathmax = UV__PATH_MAX;
+
+  return pathmax;
+}
+
+static ssize_t uv__fs_readlink(uv_fs_t* req) {
+  ssize_t maxlen;
+  ssize_t len;
+  char* buf;
+
+#if defined(_POSIX_PATH_MAX) || defined(PATH_MAX)
+  maxlen = uv__fs_pathmax_size(req->path);
+#else
+  /* We may not have a real PATH_MAX.  Read size of link.  */
+  struct stat st;
+  int ret;
+  ret = lstat(req->path, &st);
+  if (ret != 0)
+    return -1;
+  if (!S_ISLNK(st.st_mode)) {
+    errno = EINVAL;
+    return -1;
+  }
+
+  maxlen = st.st_size;
+
+  /* According to readlink(2) lstat can report st_size == 0
+     for some symlinks, such as those in /proc or /sys.  */
+  if (maxlen == 0)
+    maxlen = uv__fs_pathmax_size(req->path);
+#endif
+
+  buf = (char*)uv__malloc(maxlen);
+
+  if (buf == NULL) {
+    errno = ENOMEM;
+    return -1;
+  }
+
+#if defined(__MVS__)
+  len = os390_readlink(req->path, buf, maxlen);
+#else
+  len = readlink(req->path, buf, maxlen);
+#endif
+
+  if (len == -1) {
+    uv__free(buf);
+    return -1;
+  }
+
+  /* Uncommon case: resize to make room for the trailing nul byte. */
+  if (len == maxlen) {
+    buf = (char*)uv__reallocf(buf, len + 1);
+
+    if (buf == NULL)
+      return -1;
+  }
+
+  buf[len] = '\0';
+  req->ptr = buf;
+
+  return 0;
+}
+
+static ssize_t uv__fs_realpath(uv_fs_t* req) {
+  char* buf;
+
+#if defined(_POSIX_VERSION) && _POSIX_VERSION >= 200809L
+  buf = realpath(req->path, NULL);
+  if (buf == NULL)
+    return -1;
+#else
+  ssize_t len;
+
+  len = uv__fs_pathmax_size(req->path);
+  buf = (char*)uv__malloc(len + 1);
+
+  if (buf == NULL) {
+    errno = ENOMEM;
+    return -1;
+  }
+
+  if (realpath(req->path, buf) == NULL) {
+    uv__free(buf);
+    return -1;
+  }
+#endif
+
+  req->ptr = buf;
+
+  return 0;
+}
+
+static ssize_t uv__fs_sendfile_emul(uv_fs_t* req) {
+  struct pollfd pfd;
+  int use_pread;
+  off_t offset;
+  ssize_t nsent;
+  ssize_t nread;
+  ssize_t nwritten;
+  size_t buflen;
+  size_t len;
+  ssize_t n;
+  int in_fd;
+  int out_fd;
+  char buf[8192];
+
+  len = req->bufsml[0].len;
+  in_fd = req->flags;
+  out_fd = req->file;
+  offset = req->off;
+  use_pread = 1;
+
+  /* Here are the rules regarding errors:
+   *
+   * 1. Read errors are reported only if nsent==0, otherwise we return nsent.
+   *    The user needs to know that some data has already been sent, to stop
+   *    them from sending it twice.
+   *
+   * 2. Write errors are always reported. Write errors are bad because they
+   *    mean data loss: we've read data but now we can't write it out.
+   *
+   * We try to use pread() and fall back to regular read() if the source fd
+   * doesn't support positional reads, for example when it's a pipe fd.
+   *
+   * If we get EAGAIN when writing to the target fd, we poll() on it until
+   * it becomes writable again.
+   *
+   * FIXME: If we get a write error when use_pread==1, it should be safe to
+   *        return the number of sent bytes instead of an error because pread()
+   *        is, in theory, idempotent. However, special files in /dev or /proc
+   *        may support pread() but not necessarily return the same data on
+   *        successive reads.
+   *
+   * FIXME: There is no way now to signal that we managed to send *some* data
+   *        before a write error.
+   */
+  for (nsent = 0; (size_t) nsent < len; ) {
+    buflen = len - nsent;
+
+    if (buflen > sizeof(buf))
+      buflen = sizeof(buf);
+
+    do
+      if (use_pread)
+        nread = pread(in_fd, buf, buflen, offset);
+      else
+        nread = read(in_fd, buf, buflen);
+    while (nread == -1 && errno == EINTR);
+
+    if (nread == 0)
+      goto out;
+
+    if (nread == -1) {
+      if (use_pread && nsent == 0 && (errno == EIO || errno == ESPIPE)) {
+        use_pread = 0;
+        continue;
+      }
+
+      if (nsent == 0)
+        nsent = -1;
+
+      goto out;
+    }
+
+    for (nwritten = 0; nwritten < nread; ) {
+      do
+        n = write(out_fd, buf + nwritten, nread - nwritten);
+      while (n == -1 && errno == EINTR);
+
+      if (n != -1) {
+        nwritten += n;
+        continue;
+      }
+
+      if (errno != EAGAIN && errno != EWOULDBLOCK) {
+        nsent = -1;
+        goto out;
+      }
+
+      pfd.fd = out_fd;
+      pfd.events = POLLOUT;
+      pfd.revents = 0;
+
+      do
+        n = poll(&pfd, 1, -1);
+      while (n == -1 && errno == EINTR);
+
+      if (n == -1 || (pfd.revents & ~POLLOUT) != 0) {
+        errno = EIO;
+        nsent = -1;
+        goto out;
+      }
+    }
+
+    offset += nread;
+    nsent += nread;
+  }
+
+out:
+  if (nsent != -1)
+    req->off = offset;
+
+  return nsent;
+}
+
+
+#ifdef __linux__
+static unsigned uv__kernel_version(void) {
+  static unsigned cached_version;
+  struct utsname u;
+  unsigned version;
+  unsigned major;
+  unsigned minor;
+  unsigned patch;
+
+  version = uv__load_relaxed(&cached_version);
+  if (version != 0)
+    return version;
+
+  if (-1 == uname(&u))
+    return 0;
+
+  if (3 != sscanf(u.release, "%u.%u.%u", &major, &minor, &patch))
+    return 0;
+
+  version = major * 65536 + minor * 256 + patch;
+  uv__store_relaxed(&cached_version, version);
+
+  return version;
+}
+
+
+/* Pre-4.20 kernels have a bug where CephFS uses the RADOS copy-from command
+ * in copy_file_range() when it shouldn't. There is no workaround except to
+ * fall back to a regular copy.
+ */
+static int uv__is_buggy_cephfs(int fd) {
+  struct statfs s;
+
+  if (-1 == fstatfs(fd, &s))
+    return 0;
+
+  if (s.f_type != /* CephFS */ 0xC36400)
+    return 0;
+
+  return uv__kernel_version() < /* 4.20.0 */ 0x041400;
+}
+
+
+static int uv__is_cifs_or_smb(int fd) {
+  struct statfs s;
+
+  if (-1 == fstatfs(fd, &s))
+    return 0;
+
+  switch ((unsigned) s.f_type) {
+  case 0x0000517Bu:  /* SMB */
+  case 0xFE534D42u:  /* SMB2 */
+  case 0xFF534D42u:  /* CIFS */
+    return 1;
+  }
+
+  return 0;
+}
+
+
+static ssize_t uv__fs_try_copy_file_range(int in_fd, off_t* off,
+                                          int out_fd, size_t len) {
+  static int no_copy_file_range_support;
+  ssize_t r;
+
+  if (uv__load_relaxed(&no_copy_file_range_support)) {
+    errno = ENOSYS;
+    return -1;
+  }
+
+  r = uv__fs_copy_file_range(in_fd, off, out_fd, NULL, len, 0);
+
+  if (r != -1)
+    return r;
+
+  switch (errno) {
+  case EACCES:
+    /* Pre-4.20 kernels have a bug where CephFS uses the RADOS
+     * copy-from command when it shouldn't.
+     */
+    if (uv__is_buggy_cephfs(in_fd))
+      errno = ENOSYS;  /* Use fallback. */
+    break;
+  case ENOSYS:
+    uv__store_relaxed(&no_copy_file_range_support, 1);
+    break;
+  case EPERM:
+    /* It's been reported that CIFS spuriously fails.
+     * Consider it a transient error.
+     */
+    if (uv__is_cifs_or_smb(out_fd))
+      errno = ENOSYS;  /* Use fallback. */
+    break;
+  case ENOTSUP:
+  case EXDEV:
+    /* ENOTSUP - it could work on another file system type.
+     * EXDEV - it will not work when in_fd and out_fd are not on the same
+     *         mounted filesystem (pre Linux 5.3)
+     */
+    errno = ENOSYS;  /* Use fallback. */
+    break;
+  }
+
+  return -1;
+}
+
+#endif  /* __linux__ */
+
+
+static ssize_t uv__fs_sendfile(uv_fs_t* req) {
+  int in_fd;
+  int out_fd;
+
+  in_fd = req->flags;
+  out_fd = req->file;
+
+#if defined(__linux__) || defined(__sun)
+  {
+    off_t off;
+    ssize_t r;
+    size_t len;
+    int try_sendfile;
+
+    off = req->off;
+    len = req->bufsml[0].len;
+    try_sendfile = 1;
+
+#ifdef __linux__
+    r = uv__fs_try_copy_file_range(in_fd, &off, out_fd, len);
+    try_sendfile = (r == -1 && errno == ENOSYS);
+#endif
+
+    if (try_sendfile)
+      r = sendfile(out_fd, in_fd, &off, len);
+
+    /* sendfile() on SunOS returns EINVAL if the target fd is not a socket but
+     * it still writes out data. Fortunately, we can detect it by checking if
+     * the offset has been updated.
+     */
+    if (r != -1 || off > req->off) {
+      r = off - req->off;
+      req->off = off;
+      return r;
+    }
+
+    if (errno == EINVAL ||
+        errno == EIO ||
+        errno == ENOTSOCK ||
+        errno == EXDEV) {
+      errno = 0;
+      return uv__fs_sendfile_emul(req);
+    }
+
+    return -1;
+  }
+#elif defined(__APPLE__)           || \
+      defined(__DragonFly__)       || \
+      defined(__FreeBSD__)         || \
+      defined(__FreeBSD_kernel__)
+  {
+    off_t len;
+    ssize_t r;
+
+    /* sendfile() on FreeBSD and Darwin returns EAGAIN if the target fd is in
+     * non-blocking mode and not all data could be written. If a non-zero
+     * number of bytes have been sent, we don't consider it an error.
+     */
+
+#if defined(__FreeBSD__) || defined(__DragonFly__)
+#if defined(__FreeBSD__)
+    off_t off;
+
+    off = req->off;
+    r = uv__fs_copy_file_range(in_fd, &off, out_fd, NULL, req->bufsml[0].len, 0);
+    if (r >= 0) {
+        r = off - req->off;
+        req->off = off;
+        return r;
+    }
+#endif
+    len = 0;
+    r = sendfile(in_fd, out_fd, req->off, req->bufsml[0].len, NULL, &len, 0);
+#elif defined(__FreeBSD_kernel__)
+    len = 0;
+    r = bsd_sendfile(in_fd,
+                     out_fd,
+                     req->off,
+                     req->bufsml[0].len,
+                     NULL,
+                     &len,
+                     0);
+#else
+    /* The darwin sendfile takes len as an input for the length to send,
+     * so make sure to initialize it with the caller's value. */
+    len = req->bufsml[0].len;
+    r = sendfile(in_fd, out_fd, req->off, &len, NULL, 0);
+#endif
+
+     /*
+     * The man page for sendfile(2) on DragonFly states that `len` contains
+     * a meaningful value ONLY in case of EAGAIN and EINTR.
+     * Nothing is said about it's value in case of other errors, so better
+     * not depend on the potential wrong assumption that is was not modified
+     * by the syscall.
+     */
+    if (r == 0 || ((errno == EAGAIN || errno == EINTR) && len != 0)) {
+      req->off += len;
+      return (ssize_t) len;
+    }
+
+    if (errno == EINVAL ||
+        errno == EIO ||
+        errno == ENOTSOCK ||
+        errno == EXDEV) {
+      errno = 0;
+      return uv__fs_sendfile_emul(req);
+    }
+
+    return -1;
+  }
+#else
+  /* Squelch compiler warnings. */
+  (void) &in_fd;
+  (void) &out_fd;
+
+  return uv__fs_sendfile_emul(req);
+#endif
+}
+
+
+static ssize_t uv__fs_utime(uv_fs_t* req) {
+#if defined(__linux__)                                                         \
+    || defined(_AIX71)                                                         \
+    || defined(__sun)                                                          \
+    || defined(__HAIKU__)
+  struct timespec ts[2];
+  ts[0] = uv__fs_to_timespec(req->atime);
+  ts[1] = uv__fs_to_timespec(req->mtime);
+  return utimensat(AT_FDCWD, req->path, ts, 0);
+#elif defined(__APPLE__)                                                      \
+    || defined(__DragonFly__)                                                 \
+    || defined(__FreeBSD__)                                                   \
+    || defined(__FreeBSD_kernel__)                                            \
+    || defined(__NetBSD__)                                                    \
+    || defined(__OpenBSD__)
+  struct timeval tv[2];
+  tv[0] = uv__fs_to_timeval(req->atime);
+  tv[1] = uv__fs_to_timeval(req->mtime);
+  return utimes(req->path, tv);
+#elif defined(_AIX)                                                           \
+    && !defined(_AIX71)
+  struct utimbuf buf;
+  buf.actime = req->atime;
+  buf.modtime = req->mtime;
+  return utime(req->path, &buf);
+#elif defined(__MVS__)
+  attrib_t atr;
+  memset(&atr, 0, sizeof(atr));
+  atr.att_mtimechg = 1;
+  atr.att_atimechg = 1;
+  atr.att_mtime = req->mtime;
+  atr.att_atime = req->atime;
+  return __lchattr((char*) req->path, &atr, sizeof(atr));
+#else
+  errno = ENOSYS;
+  return -1;
+#endif
+}
+
+
+static ssize_t uv__fs_lutime(uv_fs_t* req) {
+#if defined(__linux__)            ||                                           \
+    defined(_AIX71)               ||                                           \
+    defined(__sun)                ||                                           \
+    defined(__HAIKU__)            ||                                           \
+    defined(__GNU__)              ||                                           \
+    defined(__OpenBSD__)
+  struct timespec ts[2];
+  ts[0] = uv__fs_to_timespec(req->atime);
+  ts[1] = uv__fs_to_timespec(req->mtime);
+  return utimensat(AT_FDCWD, req->path, ts, AT_SYMLINK_NOFOLLOW);
+#elif defined(__APPLE__)          ||                                          \
+      defined(__DragonFly__)      ||                                          \
+      defined(__FreeBSD__)        ||                                          \
+      defined(__FreeBSD_kernel__) ||                                          \
+      defined(__NetBSD__)
+  struct timeval tv[2];
+  tv[0] = uv__fs_to_timeval(req->atime);
+  tv[1] = uv__fs_to_timeval(req->mtime);
+  return lutimes(req->path, tv);
+#else
+  errno = ENOSYS;
+  return -1;
+#endif
+}
+
+
+static ssize_t uv__fs_write(uv_fs_t* req) {
+#if defined(__linux__)
+  static int no_pwritev;
+#endif
+  ssize_t r;
+
+  /* Serialize writes on OS X, concurrent write() and pwrite() calls result in
+   * data loss. We can't use a per-file descriptor lock, the descriptor may be
+   * a dup().
+   */
+#if defined(__APPLE__)
+  static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
+
+  if (pthread_mutex_lock(&lock))
+    abort();
+#endif
+
+  if (req->off < 0) {
+    if (req->nbufs == 1)
+      r = write(req->file, req->bufs[0].base, req->bufs[0].len);
+    else
+      r = writev(req->file, (struct iovec*) req->bufs, req->nbufs);
+  } else {
+    if (req->nbufs == 1) {
+      r = pwrite(req->file, req->bufs[0].base, req->bufs[0].len, req->off);
+      goto done;
+    }
+#if HAVE_PREADV
+    r = pwritev(req->file, (struct iovec*) req->bufs, req->nbufs, req->off);
+#else
+# if defined(__linux__)
+    if (no_pwritev) retry:
+# endif
+    {
+      r = pwrite(req->file, req->bufs[0].base, req->bufs[0].len, req->off);
+    }
+# if defined(__linux__)
+    else {
+      r = uv__pwritev(req->file,
+                      (struct iovec*) req->bufs,
+                      req->nbufs,
+                      req->off);
+      if (r == -1 && errno == ENOSYS) {
+        no_pwritev = 1;
+        goto retry;
+      }
+    }
+# endif
+#endif
+  }
+
+done:
+#if defined(__APPLE__)
+  if (pthread_mutex_unlock(&lock))
+    abort();
+#endif
+
+  return r;
+}
+
+static ssize_t uv__fs_copyfile(uv_fs_t* req) {
+  uv_fs_t fs_req;
+  uv_file srcfd;
+  uv_file dstfd;
+  struct stat src_statsbuf;
+  struct stat dst_statsbuf;
+  int dst_flags;
+  int result;
+  int err;
+  off_t bytes_to_send;
+  off_t in_offset;
+  off_t bytes_written;
+  size_t bytes_chunk;
+
+  dstfd = -1;
+  err = 0;
+
+  /* Open the source file. */
+  srcfd = uv_fs_open(NULL, &fs_req, req->path, O_RDONLY, 0, NULL);
+  uv_fs_req_cleanup(&fs_req);
+
+  if (srcfd < 0)
+    return srcfd;
+
+  /* Get the source file's mode. */
+  if (fstat(srcfd, &src_statsbuf)) {
+    err = UV__ERR(errno);
+    goto out;
+  }
+
+  dst_flags = O_WRONLY | O_CREAT;
+
+  if (req->flags & UV_FS_COPYFILE_EXCL)
+    dst_flags |= O_EXCL;
+
+  /* Open the destination file. */
+  dstfd = uv_fs_open(NULL,
+                     &fs_req,
+                     req->new_path,
+                     dst_flags,
+                     src_statsbuf.st_mode,
+                     NULL);
+  uv_fs_req_cleanup(&fs_req);
+
+  if (dstfd < 0) {
+    err = dstfd;
+    goto out;
+  }
+
+  /* If the file is not being opened exclusively, verify that the source and
+     destination are not the same file. If they are the same, bail out early. */
+  if ((req->flags & UV_FS_COPYFILE_EXCL) == 0) {
+    /* Get the destination file's mode. */
+    if (fstat(dstfd, &dst_statsbuf)) {
+      err = UV__ERR(errno);
+      goto out;
+    }
+
+    /* Check if srcfd and dstfd refer to the same file */
+    if (src_statsbuf.st_dev == dst_statsbuf.st_dev &&
+        src_statsbuf.st_ino == dst_statsbuf.st_ino) {
+      goto out;
+    }
+
+    /* Truncate the file in case the destination already existed. */
+    if (ftruncate(dstfd, 0) != 0) {
+      err = UV__ERR(errno);
+      goto out;
+    }
+  }
+
+  if (fchmod(dstfd, src_statsbuf.st_mode) == -1) {
+    err = UV__ERR(errno);
+#ifdef __linux__
+    /* fchmod() on CIFS shares always fails with EPERM unless the share is
+     * mounted with "noperm". As fchmod() is a meaningless operation on such
+     * shares anyway, detect that condition and squelch the error.
+     */
+    if (err != UV_EPERM)
+      goto out;
+
+    if (!uv__is_cifs_or_smb(dstfd))
+      goto out;
+
+    err = 0;
+#else  /* !__linux__ */
+    goto out;
+#endif  /* !__linux__ */
+  }
+
+#ifdef FICLONE
+  if (req->flags & UV_FS_COPYFILE_FICLONE ||
+      req->flags & UV_FS_COPYFILE_FICLONE_FORCE) {
+    if (ioctl(dstfd, FICLONE, srcfd) == 0) {
+      /* ioctl() with FICLONE succeeded. */
+      goto out;
+    }
+    /* If an error occurred and force was set, return the error to the caller;
+     * fall back to sendfile() when force was not set. */
+    if (req->flags & UV_FS_COPYFILE_FICLONE_FORCE) {
+      err = UV__ERR(errno);
+      goto out;
+    }
+  }
+#else
+  if (req->flags & UV_FS_COPYFILE_FICLONE_FORCE) {
+    err = UV_ENOSYS;
+    goto out;
+  }
+#endif
+
+  bytes_to_send = src_statsbuf.st_size;
+  in_offset = 0;
+  while (bytes_to_send != 0) {
+    bytes_chunk = SSIZE_MAX;
+    if (bytes_to_send < (off_t) bytes_chunk)
+      bytes_chunk = bytes_to_send;
+    uv_fs_sendfile(NULL, &fs_req, dstfd, srcfd, in_offset, bytes_chunk, NULL);
+    bytes_written = fs_req.result;
+    uv_fs_req_cleanup(&fs_req);
+
+    if (bytes_written < 0) {
+      err = bytes_written;
+      break;
+    }
+
+    bytes_to_send -= bytes_written;
+    in_offset += bytes_written;
+  }
+
+out:
+  if (err < 0)
+    result = err;
+  else
+    result = 0;
+
+  /* Close the source file. */
+  err = uv__close_nocheckstdio(srcfd);
+
+  /* Don't overwrite any existing errors. */
+  if (err != 0 && result == 0)
+    result = err;
+
+  /* Close the destination file if it is open. */
+  if (dstfd >= 0) {
+    err = uv__close_nocheckstdio(dstfd);
+
+    /* Don't overwrite any existing errors. */
+    if (err != 0 && result == 0)
+      result = err;
+
+    /* Remove the destination file if something went wrong. */
+    if (result != 0) {
+      uv_fs_unlink(NULL, &fs_req, req->new_path, NULL);
+      /* Ignore the unlink return value, as an error already happened. */
+      uv_fs_req_cleanup(&fs_req);
+    }
+  }
+
+  if (result == 0)
+    return 0;
+
+  errno = UV__ERR(result);
+  return -1;
+}
+
+static void uv__to_stat(struct stat* src, uv_stat_t* dst) {
+  dst->st_dev = src->st_dev;
+  dst->st_mode = src->st_mode;
+  dst->st_nlink = src->st_nlink;
+  dst->st_uid = src->st_uid;
+  dst->st_gid = src->st_gid;
+  dst->st_rdev = src->st_rdev;
+  dst->st_ino = src->st_ino;
+  dst->st_size = src->st_size;
+  dst->st_blksize = src->st_blksize;
+  dst->st_blocks = src->st_blocks;
+
+#if defined(__APPLE__)
+  dst->st_atim.tv_sec = src->st_atimespec.tv_sec;
+  dst->st_atim.tv_nsec = src->st_atimespec.tv_nsec;
+  dst->st_mtim.tv_sec = src->st_mtimespec.tv_sec;
+  dst->st_mtim.tv_nsec = src->st_mtimespec.tv_nsec;
+  dst->st_ctim.tv_sec = src->st_ctimespec.tv_sec;
+  dst->st_ctim.tv_nsec = src->st_ctimespec.tv_nsec;
+  dst->st_birthtim.tv_sec = src->st_birthtimespec.tv_sec;
+  dst->st_birthtim.tv_nsec = src->st_birthtimespec.tv_nsec;
+  dst->st_flags = src->st_flags;
+  dst->st_gen = src->st_gen;
+#elif defined(__ANDROID__)
+  dst->st_atim.tv_sec = src->st_atime;
+  dst->st_atim.tv_nsec = src->st_atimensec;
+  dst->st_mtim.tv_sec = src->st_mtime;
+  dst->st_mtim.tv_nsec = src->st_mtimensec;
+  dst->st_ctim.tv_sec = src->st_ctime;
+  dst->st_ctim.tv_nsec = src->st_ctimensec;
+  dst->st_birthtim.tv_sec = src->st_ctime;
+  dst->st_birthtim.tv_nsec = src->st_ctimensec;
+  dst->st_flags = 0;
+  dst->st_gen = 0;
+#elif !defined(_AIX) &&         \
+    !defined(__MVS__) && (      \
+    defined(__DragonFly__)   || \
+    defined(__FreeBSD__)     || \
+    defined(__OpenBSD__)     || \
+    defined(__NetBSD__)      || \
+    defined(_GNU_SOURCE)     || \
+    defined(_BSD_SOURCE)     || \
+    defined(_SVID_SOURCE)    || \
+    defined(_XOPEN_SOURCE)   || \
+    defined(_DEFAULT_SOURCE))
+  dst->st_atim.tv_sec = src->st_atim.tv_sec;
+  dst->st_atim.tv_nsec = src->st_atim.tv_nsec;
+  dst->st_mtim.tv_sec = src->st_mtim.tv_sec;
+  dst->st_mtim.tv_nsec = src->st_mtim.tv_nsec;
+  dst->st_ctim.tv_sec = src->st_ctim.tv_sec;
+  dst->st_ctim.tv_nsec = src->st_ctim.tv_nsec;
+# if defined(__FreeBSD__)    || \
+     defined(__NetBSD__)
+  dst->st_birthtim.tv_sec = src->st_birthtim.tv_sec;
+  dst->st_birthtim.tv_nsec = src->st_birthtim.tv_nsec;
+  dst->st_flags = src->st_flags;
+  dst->st_gen = src->st_gen;
+# else
+  dst->st_birthtim.tv_sec = src->st_ctim.tv_sec;
+  dst->st_birthtim.tv_nsec = src->st_ctim.tv_nsec;
+  dst->st_flags = 0;
+  dst->st_gen = 0;
+# endif
+#else
+  dst->st_atim.tv_sec = src->st_atime;
+  dst->st_atim.tv_nsec = 0;
+  dst->st_mtim.tv_sec = src->st_mtime;
+  dst->st_mtim.tv_nsec = 0;
+  dst->st_ctim.tv_sec = src->st_ctime;
+  dst->st_ctim.tv_nsec = 0;
+  dst->st_birthtim.tv_sec = src->st_ctime;
+  dst->st_birthtim.tv_nsec = 0;
+  dst->st_flags = 0;
+  dst->st_gen = 0;
+#endif
+}
+
+
+static int uv__fs_statx(int fd,
+                        const char* path,
+                        int is_fstat,
+                        int is_lstat,
+                        uv_stat_t* buf) {
+  STATIC_ASSERT(UV_ENOSYS != -1);
+#ifdef __linux__
+  static int no_statx;
+  struct uv__statx statxbuf;
+  int dirfd;
+  int flags;
+  int mode;
+  int rc;
+
+  if (uv__load_relaxed(&no_statx))
+    return UV_ENOSYS;
+
+  dirfd = AT_FDCWD;
+  flags = 0; /* AT_STATX_SYNC_AS_STAT */
+  mode = 0xFFF; /* STATX_BASIC_STATS + STATX_BTIME */
+
+  if (is_fstat) {
+    dirfd = fd;
+    flags |= 0x1000; /* AT_EMPTY_PATH */
+  }
+
+  if (is_lstat)
+    flags |= AT_SYMLINK_NOFOLLOW;
+
+  rc = uv__statx(dirfd, path, flags, mode, &statxbuf);
+
+  switch (rc) {
+  case 0:
+    break;
+  case -1:
+    /* EPERM happens when a seccomp filter rejects the system call.
+     * Has been observed with libseccomp < 2.3.3 and docker < 18.04.
+     * EOPNOTSUPP is used on DVS exported filesystems
+     */
+    if (errno != EINVAL && errno != EPERM && errno != ENOSYS && errno != EOPNOTSUPP)
+      return -1;
+    /* Fall through. */
+  default:
+    /* Normally on success, zero is returned and On error, -1 is returned.
+     * Observed on S390 RHEL running in a docker container with statx not
+     * implemented, rc might return 1 with 0 set as the error code in which
+     * case we return ENOSYS.
+     */
+    uv__store_relaxed(&no_statx, 1);
+    return UV_ENOSYS;
+  }
+
+  buf->st_dev = makedev(statxbuf.stx_dev_major, statxbuf.stx_dev_minor);
+  buf->st_mode = statxbuf.stx_mode;
+  buf->st_nlink = statxbuf.stx_nlink;
+  buf->st_uid = statxbuf.stx_uid;
+  buf->st_gid = statxbuf.stx_gid;
+  buf->st_rdev = makedev(statxbuf.stx_rdev_major, statxbuf.stx_rdev_minor);
+  buf->st_ino = statxbuf.stx_ino;
+  buf->st_size = statxbuf.stx_size;
+  buf->st_blksize = statxbuf.stx_blksize;
+  buf->st_blocks = statxbuf.stx_blocks;
+  buf->st_atim.tv_sec = statxbuf.stx_atime.tv_sec;
+  buf->st_atim.tv_nsec = statxbuf.stx_atime.tv_nsec;
+  buf->st_mtim.tv_sec = statxbuf.stx_mtime.tv_sec;
+  buf->st_mtim.tv_nsec = statxbuf.stx_mtime.tv_nsec;
+  buf->st_ctim.tv_sec = statxbuf.stx_ctime.tv_sec;
+  buf->st_ctim.tv_nsec = statxbuf.stx_ctime.tv_nsec;
+  buf->st_birthtim.tv_sec = statxbuf.stx_btime.tv_sec;
+  buf->st_birthtim.tv_nsec = statxbuf.stx_btime.tv_nsec;
+  buf->st_flags = 0;
+  buf->st_gen = 0;
+
+  return 0;
+#else
+  return UV_ENOSYS;
+#endif /* __linux__ */
+}
+
+
+static int uv__fs_stat(const char *path, uv_stat_t *buf) {
+  struct stat pbuf;
+  int ret;
+
+  ret = uv__fs_statx(-1, path, /* is_fstat */ 0, /* is_lstat */ 0, buf);
+  if (ret != UV_ENOSYS)
+    return ret;
+
+  ret = stat(path, &pbuf);
+  if (ret == 0)
+    uv__to_stat(&pbuf, buf);
+
+  return ret;
+}
+
+
+static int uv__fs_lstat(const char *path, uv_stat_t *buf) {
+  struct stat pbuf;
+  int ret;
+
+  ret = uv__fs_statx(-1, path, /* is_fstat */ 0, /* is_lstat */ 1, buf);
+  if (ret != UV_ENOSYS)
+    return ret;
+
+  ret = lstat(path, &pbuf);
+  if (ret == 0)
+    uv__to_stat(&pbuf, buf);
+
+  return ret;
+}
+
+
+static int uv__fs_fstat(int fd, uv_stat_t *buf) {
+  struct stat pbuf;
+  int ret;
+
+  ret = uv__fs_statx(fd, "", /* is_fstat */ 1, /* is_lstat */ 0, buf);
+  if (ret != UV_ENOSYS)
+    return ret;
+
+  ret = fstat(fd, &pbuf);
+  if (ret == 0)
+    uv__to_stat(&pbuf, buf);
+
+  return ret;
+}
+
+static size_t uv__fs_buf_offset(uv_buf_t* bufs, size_t size) {
+  size_t offset;
+  /* Figure out which bufs are done */
+  for (offset = 0; size > 0 && bufs[offset].len <= size; ++offset)
+    size -= bufs[offset].len;
+
+  /* Fix a partial read/write */
+  if (size > 0) {
+    bufs[offset].base += size;
+    bufs[offset].len -= size;
+  }
+  return offset;
+}
+
+static ssize_t uv__fs_write_all(uv_fs_t* req) {
+  unsigned int iovmax;
+  unsigned int nbufs;
+  uv_buf_t* bufs;
+  ssize_t total;
+  ssize_t result;
+
+  iovmax = uv__getiovmax();
+  nbufs = req->nbufs;
+  bufs = req->bufs;
+  total = 0;
+
+  while (nbufs > 0) {
+    req->nbufs = nbufs;
+    if (req->nbufs > iovmax)
+      req->nbufs = iovmax;
+
+    do
+      result = uv__fs_write(req);
+    while (result < 0 && errno == EINTR);
+
+    if (result <= 0) {
+      if (total == 0)
+        total = result;
+      break;
+    }
+
+    if (req->off >= 0)
+      req->off += result;
+
+    req->nbufs = uv__fs_buf_offset(req->bufs, result);
+    req->bufs += req->nbufs;
+    nbufs -= req->nbufs;
+    total += result;
+  }
+
+  if (bufs != req->bufsml)
+    uv__free(bufs);
+
+  req->bufs = NULL;
+  req->nbufs = 0;
+
+  return total;
+}
+
+
+static void uv__fs_work(struct uv__work* w) {
+  int retry_on_eintr;
+  uv_fs_t* req;
+  ssize_t r;
+
+  req = container_of(w, uv_fs_t, work_req);
+  retry_on_eintr = !(req->fs_type == UV_FS_CLOSE ||
+                     req->fs_type == UV_FS_READ);
+
+  do {
+    errno = 0;
+
+#define X(type, action)                                                       \
+  case UV_FS_ ## type:                                                        \
+    r = action;                                                               \
+    break;
+
+    switch (req->fs_type) {
+    X(ACCESS, access(req->path, req->flags));
+    X(CHMOD, chmod(req->path, req->mode));
+    X(CHOWN, chown(req->path, req->uid, req->gid));
+    X(CLOSE, uv__fs_close(req->file));
+    X(COPYFILE, uv__fs_copyfile(req));
+    X(FCHMOD, fchmod(req->file, req->mode));
+    X(FCHOWN, fchown(req->file, req->uid, req->gid));
+    X(LCHOWN, lchown(req->path, req->uid, req->gid));
+    X(FDATASYNC, uv__fs_fdatasync(req));
+    X(FSTAT, uv__fs_fstat(req->file, &req->statbuf));
+    X(FSYNC, uv__fs_fsync(req));
+    X(FTRUNCATE, ftruncate(req->file, req->off));
+    X(FUTIME, uv__fs_futime(req));
+    X(LUTIME, uv__fs_lutime(req));
+    X(LSTAT, uv__fs_lstat(req->path, &req->statbuf));
+    X(LINK, link(req->path, req->new_path));
+    X(MKDIR, mkdir(req->path, req->mode));
+    X(MKDTEMP, uv__fs_mkdtemp(req));
+    X(MKSTEMP, uv__fs_mkstemp(req));
+    X(OPEN, uv__fs_open(req));
+    X(READ, uv__fs_read(req));
+    X(SCANDIR, uv__fs_scandir(req));
+    X(OPENDIR, uv__fs_opendir(req));
+    X(READDIR, uv__fs_readdir(req));
+    X(CLOSEDIR, uv__fs_closedir(req));
+    X(READLINK, uv__fs_readlink(req));
+    X(REALPATH, uv__fs_realpath(req));
+    X(RENAME, rename(req->path, req->new_path));
+    X(RMDIR, rmdir(req->path));
+    X(SENDFILE, uv__fs_sendfile(req));
+    X(STAT, uv__fs_stat(req->path, &req->statbuf));
+    X(STATFS, uv__fs_statfs(req));
+    X(SYMLINK, symlink(req->path, req->new_path));
+    X(UNLINK, unlink(req->path));
+    X(UTIME, uv__fs_utime(req));
+    X(WRITE, uv__fs_write_all(req));
+    default: abort();
+    }
+#undef X
+  } while (r == -1 && errno == EINTR && retry_on_eintr);
+
+  if (r == -1)
+    req->result = UV__ERR(errno);
+  else
+    req->result = r;
+
+  if (r == 0 && (req->fs_type == UV_FS_STAT ||
+                 req->fs_type == UV_FS_FSTAT ||
+                 req->fs_type == UV_FS_LSTAT)) {
+    req->ptr = &req->statbuf;
+  }
+}
+
+
+static void uv__fs_done(struct uv__work* w, int status) {
+  uv_fs_t* req;
+
+  req = container_of(w, uv_fs_t, work_req);
+  uv__req_unregister(req->loop, req);
+
+  if (status == UV_ECANCELED) {
+    assert(req->result == 0);
+    req->result = UV_ECANCELED;
+  }
+
+  req->cb(req);
+}
+
+
+int uv_fs_access(uv_loop_t* loop,
+                 uv_fs_t* req,
+                 const char* path,
+                 int flags,
+                 uv_fs_cb cb) {
+  INIT(ACCESS);
+  PATH;
+  req->flags = flags;
+  POST;
+}
+
+
+int uv_fs_chmod(uv_loop_t* loop,
+                uv_fs_t* req,
+                const char* path,
+                int mode,
+                uv_fs_cb cb) {
+  INIT(CHMOD);
+  PATH;
+  req->mode = mode;
+  POST;
+}
+
+
+int uv_fs_chown(uv_loop_t* loop,
+                uv_fs_t* req,
+                const char* path,
+                uv_uid_t uid,
+                uv_gid_t gid,
+                uv_fs_cb cb) {
+  INIT(CHOWN);
+  PATH;
+  req->uid = uid;
+  req->gid = gid;
+  POST;
+}
+
+
+int uv_fs_close(uv_loop_t* loop, uv_fs_t* req, uv_file file, uv_fs_cb cb) {
+  INIT(CLOSE);
+  req->file = file;
+  POST;
+}
+
+
+int uv_fs_fchmod(uv_loop_t* loop,
+                 uv_fs_t* req,
+                 uv_file file,
+                 int mode,
+                 uv_fs_cb cb) {
+  INIT(FCHMOD);
+  req->file = file;
+  req->mode = mode;
+  POST;
+}
+
+
+int uv_fs_fchown(uv_loop_t* loop,
+                 uv_fs_t* req,
+                 uv_file file,
+                 uv_uid_t uid,
+                 uv_gid_t gid,
+                 uv_fs_cb cb) {
+  INIT(FCHOWN);
+  req->file = file;
+  req->uid = uid;
+  req->gid = gid;
+  POST;
+}
+
+
+int uv_fs_lchown(uv_loop_t* loop,
+                 uv_fs_t* req,
+                 const char* path,
+                 uv_uid_t uid,
+                 uv_gid_t gid,
+                 uv_fs_cb cb) {
+  INIT(LCHOWN);
+  PATH;
+  req->uid = uid;
+  req->gid = gid;
+  POST;
+}
+
+
+int uv_fs_fdatasync(uv_loop_t* loop, uv_fs_t* req, uv_file file, uv_fs_cb cb) {
+  INIT(FDATASYNC);
+  req->file = file;
+  POST;
+}
+
+
+int uv_fs_fstat(uv_loop_t* loop, uv_fs_t* req, uv_file file, uv_fs_cb cb) {
+  INIT(FSTAT);
+  req->file = file;
+  POST;
+}
+
+
+int uv_fs_fsync(uv_loop_t* loop, uv_fs_t* req, uv_file file, uv_fs_cb cb) {
+  INIT(FSYNC);
+  req->file = file;
+  POST;
+}
+
+
+int uv_fs_ftruncate(uv_loop_t* loop,
+                    uv_fs_t* req,
+                    uv_file file,
+                    int64_t off,
+                    uv_fs_cb cb) {
+  INIT(FTRUNCATE);
+  req->file = file;
+  req->off = off;
+  POST;
+}
+
+
+int uv_fs_futime(uv_loop_t* loop,
+                 uv_fs_t* req,
+                 uv_file file,
+                 double atime,
+                 double mtime,
+                 uv_fs_cb cb) {
+  INIT(FUTIME);
+  req->file = file;
+  req->atime = atime;
+  req->mtime = mtime;
+  POST;
+}
+
+int uv_fs_lutime(uv_loop_t* loop,
+                 uv_fs_t* req,
+                 const char* path,
+                 double atime,
+                 double mtime,
+                 uv_fs_cb cb) {
+  INIT(LUTIME);
+  PATH;
+  req->atime = atime;
+  req->mtime = mtime;
+  POST;
+}
+
+
+int uv_fs_lstat(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) {
+  INIT(LSTAT);
+  PATH;
+  POST;
+}
+
+
+int uv_fs_link(uv_loop_t* loop,
+               uv_fs_t* req,
+               const char* path,
+               const char* new_path,
+               uv_fs_cb cb) {
+  INIT(LINK);
+  PATH2;
+  POST;
+}
+
+
+int uv_fs_mkdir(uv_loop_t* loop,
+                uv_fs_t* req,
+                const char* path,
+                int mode,
+                uv_fs_cb cb) {
+  INIT(MKDIR);
+  PATH;
+  req->mode = mode;
+  POST;
+}
+
+
+int uv_fs_mkdtemp(uv_loop_t* loop,
+                  uv_fs_t* req,
+                  const char* tpl,
+                  uv_fs_cb cb) {
+  INIT(MKDTEMP);
+  req->path = uv__strdup(tpl);
+  if (req->path == NULL)
+    return UV_ENOMEM;
+  POST;
+}
+
+
+int uv_fs_mkstemp(uv_loop_t* loop,
+                  uv_fs_t* req,
+                  const char* tpl,
+                  uv_fs_cb cb) {
+  INIT(MKSTEMP);
+  req->path = uv__strdup(tpl);
+  if (req->path == NULL)
+    return UV_ENOMEM;
+  POST;
+}
+
+
+int uv_fs_open(uv_loop_t* loop,
+               uv_fs_t* req,
+               const char* path,
+               int flags,
+               int mode,
+               uv_fs_cb cb) {
+  INIT(OPEN);
+  PATH;
+  req->flags = flags;
+  req->mode = mode;
+  POST;
+}
+
+
+int uv_fs_read(uv_loop_t* loop, uv_fs_t* req,
+               uv_file file,
+               const uv_buf_t bufs[],
+               unsigned int nbufs,
+               int64_t off,
+               uv_fs_cb cb) {
+  INIT(READ);
+
+  if (bufs == NULL || nbufs == 0)
+    return UV_EINVAL;
+
+  req->file = file;
+
+  req->nbufs = nbufs;
+  req->bufs = req->bufsml;
+  if (nbufs > ARRAY_SIZE(req->bufsml))
+    req->bufs = (uv_buf_t*)uv__malloc(nbufs * sizeof(*bufs));
+
+  if (req->bufs == NULL)
+    return UV_ENOMEM;
+
+  memcpy(req->bufs, bufs, nbufs * sizeof(*bufs));
+
+  req->off = off;
+  POST;
+}
+
+
+int uv_fs_scandir(uv_loop_t* loop,
+                  uv_fs_t* req,
+                  const char* path,
+                  int flags,
+                  uv_fs_cb cb) {
+  INIT(SCANDIR);
+  PATH;
+  req->flags = flags;
+  POST;
+}
+
+int uv_fs_opendir(uv_loop_t* loop,
+                  uv_fs_t* req,
+                  const char* path,
+                  uv_fs_cb cb) {
+  INIT(OPENDIR);
+  PATH;
+  POST;
+}
+
+int uv_fs_readdir(uv_loop_t* loop,
+                  uv_fs_t* req,
+                  uv_dir_t* dir,
+                  uv_fs_cb cb) {
+  INIT(READDIR);
+
+  if (dir == NULL || dir->dir == NULL || dir->dirents == NULL)
+    return UV_EINVAL;
+
+  req->ptr = dir;
+  POST;
+}
+
+int uv_fs_closedir(uv_loop_t* loop,
+                   uv_fs_t* req,
+                   uv_dir_t* dir,
+                   uv_fs_cb cb) {
+  INIT(CLOSEDIR);
+
+  if (dir == NULL)
+    return UV_EINVAL;
+
+  req->ptr = dir;
+  POST;
+}
+
+int uv_fs_readlink(uv_loop_t* loop,
+                   uv_fs_t* req,
+                   const char* path,
+                   uv_fs_cb cb) {
+  INIT(READLINK);
+  PATH;
+  POST;
+}
+
+
+int uv_fs_realpath(uv_loop_t* loop,
+                  uv_fs_t* req,
+                  const char * path,
+                  uv_fs_cb cb) {
+  INIT(REALPATH);
+  PATH;
+  POST;
+}
+
+
+int uv_fs_rename(uv_loop_t* loop,
+                 uv_fs_t* req,
+                 const char* path,
+                 const char* new_path,
+                 uv_fs_cb cb) {
+  INIT(RENAME);
+  PATH2;
+  POST;
+}
+
+
+int uv_fs_rmdir(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) {
+  INIT(RMDIR);
+  PATH;
+  POST;
+}
+
+
+int uv_fs_sendfile(uv_loop_t* loop,
+                   uv_fs_t* req,
+                   uv_file out_fd,
+                   uv_file in_fd,
+                   int64_t off,
+                   size_t len,
+                   uv_fs_cb cb) {
+  INIT(SENDFILE);
+  req->flags = in_fd; /* hack */
+  req->file = out_fd;
+  req->off = off;
+  req->bufsml[0].len = len;
+  POST;
+}
+
+
+int uv_fs_stat(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) {
+  INIT(STAT);
+  PATH;
+  POST;
+}
+
+
+int uv_fs_symlink(uv_loop_t* loop,
+                  uv_fs_t* req,
+                  const char* path,
+                  const char* new_path,
+                  int flags,
+                  uv_fs_cb cb) {
+  INIT(SYMLINK);
+  PATH2;
+  req->flags = flags;
+  POST;
+}
+
+
+int uv_fs_unlink(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) {
+  INIT(UNLINK);
+  PATH;
+  POST;
+}
+
+
+int uv_fs_utime(uv_loop_t* loop,
+                uv_fs_t* req,
+                const char* path,
+                double atime,
+                double mtime,
+                uv_fs_cb cb) {
+  INIT(UTIME);
+  PATH;
+  req->atime = atime;
+  req->mtime = mtime;
+  POST;
+}
+
+
+int uv_fs_write(uv_loop_t* loop,
+                uv_fs_t* req,
+                uv_file file,
+                const uv_buf_t bufs[],
+                unsigned int nbufs,
+                int64_t off,
+                uv_fs_cb cb) {
+  INIT(WRITE);
+
+  if (bufs == NULL || nbufs == 0)
+    return UV_EINVAL;
+
+  req->file = file;
+
+  req->nbufs = nbufs;
+  req->bufs = req->bufsml;
+  if (nbufs > ARRAY_SIZE(req->bufsml))
+    req->bufs = (uv_buf_t*)uv__malloc(nbufs * sizeof(*bufs));
+
+  if (req->bufs == NULL)
+    return UV_ENOMEM;
+
+  memcpy(req->bufs, bufs, nbufs * sizeof(*bufs));
+
+  req->off = off;
+  POST;
+}
+
+
+void uv_fs_req_cleanup(uv_fs_t* req) {
+  if (req == NULL)
+    return;
+
+  /* Only necessary for asychronous requests, i.e., requests with a callback.
+   * Synchronous ones don't copy their arguments and have req->path and
+   * req->new_path pointing to user-owned memory.  UV_FS_MKDTEMP and
+   * UV_FS_MKSTEMP are the exception to the rule, they always allocate memory.
+   */
+  if (req->path != NULL &&
+      (req->cb != NULL ||
+        req->fs_type == UV_FS_MKDTEMP || req->fs_type == UV_FS_MKSTEMP))
+    uv__free((void*) req->path);  /* Memory is shared with req->new_path. */
+
+  req->path = NULL;
+  req->new_path = NULL;
+
+  if (req->fs_type == UV_FS_READDIR && req->ptr != NULL)
+    uv__fs_readdir_cleanup(req);
+
+  if (req->fs_type == UV_FS_SCANDIR && req->ptr != NULL)
+    uv__fs_scandir_cleanup(req);
+
+  if (req->bufs != req->bufsml)
+    uv__free(req->bufs);
+  req->bufs = NULL;
+
+  if (req->fs_type != UV_FS_OPENDIR && req->ptr != &req->statbuf)
+    uv__free(req->ptr);
+  req->ptr = NULL;
+}
+
+
+int uv_fs_copyfile(uv_loop_t* loop,
+                   uv_fs_t* req,
+                   const char* path,
+                   const char* new_path,
+                   int flags,
+                   uv_fs_cb cb) {
+  INIT(COPYFILE);
+
+  if (flags & ~(UV_FS_COPYFILE_EXCL |
+                UV_FS_COPYFILE_FICLONE |
+                UV_FS_COPYFILE_FICLONE_FORCE)) {
+    return UV_EINVAL;
+  }
+
+  PATH2;
+  req->flags = flags;
+  POST;
+}
+
+
+int uv_fs_statfs(uv_loop_t* loop,
+                 uv_fs_t* req,
+                 const char* path,
+                 uv_fs_cb cb) {
+  INIT(STATFS);
+  PATH;
+  POST;
+}
+
+int uv_fs_get_system_error(const uv_fs_t* req) {
+  return -req->result;
+}
diff --git a/wpinet/src/main/native/thirdparty/libuv/src/unix/fsevents.cpp b/wpinet/src/main/native/thirdparty/libuv/src/unix/fsevents.cpp
new file mode 100644
index 0000000..648c8a9
--- /dev/null
+++ b/wpinet/src/main/native/thirdparty/libuv/src/unix/fsevents.cpp
@@ -0,0 +1,916 @@
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include "uv.h"
+#include "internal.h"
+
+#if TARGET_OS_IPHONE || MAC_OS_X_VERSION_MAX_ALLOWED < 1070
+
+/* iOS (currently) doesn't provide the FSEvents-API (nor CoreServices) */
+/* macOS prior to 10.7 doesn't provide the full FSEvents API so use kqueue */
+
+int uv__fsevents_init(uv_fs_event_t* handle) {
+  return 0;
+}
+
+
+int uv__fsevents_close(uv_fs_event_t* handle) {
+  return 0;
+}
+
+
+void uv__fsevents_loop_delete(uv_loop_t* loop) {
+}
+
+#else /* TARGET_OS_IPHONE */
+
+#include "darwin-stub.h"
+
+#include <dlfcn.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <pthread.h>
+
+static const int kFSEventsModified =
+    kFSEventStreamEventFlagItemChangeOwner |
+    kFSEventStreamEventFlagItemFinderInfoMod |
+    kFSEventStreamEventFlagItemInodeMetaMod |
+    kFSEventStreamEventFlagItemModified |
+    kFSEventStreamEventFlagItemXattrMod;
+
+static const int kFSEventsRenamed =
+    kFSEventStreamEventFlagItemCreated |
+    kFSEventStreamEventFlagItemRemoved |
+    kFSEventStreamEventFlagItemRenamed;
+
+static const int kFSEventsSystem =
+    kFSEventStreamEventFlagUserDropped |
+    kFSEventStreamEventFlagKernelDropped |
+    kFSEventStreamEventFlagEventIdsWrapped |
+    kFSEventStreamEventFlagHistoryDone |
+    kFSEventStreamEventFlagMount |
+    kFSEventStreamEventFlagUnmount |
+    kFSEventStreamEventFlagRootChanged;
+
+typedef struct uv__fsevents_event_s uv__fsevents_event_t;
+typedef struct uv__cf_loop_signal_s uv__cf_loop_signal_t;
+typedef struct uv__cf_loop_state_s uv__cf_loop_state_t;
+
+enum uv__cf_loop_signal_type_e {
+  kUVCFLoopSignalRegular,
+  kUVCFLoopSignalClosing
+};
+typedef enum uv__cf_loop_signal_type_e uv__cf_loop_signal_type_t;
+
+struct uv__cf_loop_signal_s {
+  QUEUE member;
+  uv_fs_event_t* handle;
+  uv__cf_loop_signal_type_t type;
+};
+
+struct uv__fsevents_event_s {
+  QUEUE member;
+  int events;
+  char path[1];
+};
+
+struct uv__cf_loop_state_s {
+  CFRunLoopRef loop;
+  CFRunLoopSourceRef signal_source;
+  int fsevent_need_reschedule;
+  FSEventStreamRef fsevent_stream;
+  uv_sem_t fsevent_sem;
+  uv_mutex_t fsevent_mutex;
+  void* fsevent_handles[2];
+  unsigned int fsevent_handle_count;
+};
+
+/* Forward declarations */
+static void uv__cf_loop_cb(void* arg);
+static void* uv__cf_loop_runner(void* arg);
+static int uv__cf_loop_signal(uv_loop_t* loop,
+                              uv_fs_event_t* handle,
+                              uv__cf_loop_signal_type_t type);
+
+/* Lazy-loaded by uv__fsevents_global_init(). */
+static CFArrayRef (*pCFArrayCreate)(CFAllocatorRef,
+                                    const void**,
+                                    CFIndex,
+                                    const CFArrayCallBacks*);
+static void (*pCFRelease)(CFTypeRef);
+static void (*pCFRunLoopAddSource)(CFRunLoopRef,
+                                   CFRunLoopSourceRef,
+                                   CFStringRef);
+static CFRunLoopRef (*pCFRunLoopGetCurrent)(void);
+static void (*pCFRunLoopRemoveSource)(CFRunLoopRef,
+                                      CFRunLoopSourceRef,
+                                      CFStringRef);
+static void (*pCFRunLoopRun)(void);
+static CFRunLoopSourceRef (*pCFRunLoopSourceCreate)(CFAllocatorRef,
+                                                    CFIndex,
+                                                    CFRunLoopSourceContext*);
+static void (*pCFRunLoopSourceSignal)(CFRunLoopSourceRef);
+static void (*pCFRunLoopStop)(CFRunLoopRef);
+static void (*pCFRunLoopWakeUp)(CFRunLoopRef);
+static CFStringRef (*pCFStringCreateWithFileSystemRepresentation)(
+    CFAllocatorRef,
+    const char*);
+static CFStringEncoding (*pCFStringGetSystemEncoding)(void);
+static CFStringRef (*pkCFRunLoopDefaultMode);
+static FSEventStreamRef (*pFSEventStreamCreate)(CFAllocatorRef,
+                                                FSEventStreamCallback,
+                                                FSEventStreamContext*,
+                                                CFArrayRef,
+                                                FSEventStreamEventId,
+                                                CFTimeInterval,
+                                                FSEventStreamCreateFlags);
+static void (*pFSEventStreamFlushSync)(FSEventStreamRef);
+static void (*pFSEventStreamInvalidate)(FSEventStreamRef);
+static void (*pFSEventStreamRelease)(FSEventStreamRef);
+static void (*pFSEventStreamScheduleWithRunLoop)(FSEventStreamRef,
+                                                 CFRunLoopRef,
+                                                 CFStringRef);
+static int (*pFSEventStreamStart)(FSEventStreamRef);
+static void (*pFSEventStreamStop)(FSEventStreamRef);
+
+#define UV__FSEVENTS_PROCESS(handle, block)                                   \
+    do {                                                                      \
+      QUEUE events;                                                           \
+      QUEUE* q;                                                               \
+      uv__fsevents_event_t* event;                                            \
+      int err;                                                                \
+      uv_mutex_lock(&(handle)->cf_mutex);                                     \
+      /* Split-off all events and empty original queue */                     \
+      QUEUE_MOVE(&(handle)->cf_events, &events);                              \
+      /* Get error (if any) and zero original one */                          \
+      err = (handle)->cf_error;                                               \
+      (handle)->cf_error = 0;                                                 \
+      uv_mutex_unlock(&(handle)->cf_mutex);                                   \
+      /* Loop through events, deallocating each after processing */           \
+      while (!QUEUE_EMPTY(&events)) {                                         \
+        q = QUEUE_HEAD(&events);                                              \
+        event = QUEUE_DATA(q, uv__fsevents_event_t, member);                  \
+        QUEUE_REMOVE(q);                                                      \
+        /* NOTE: Checking uv__is_active() is required here, because handle    \
+         * callback may close handle and invoking it after it will lead to    \
+         * incorrect behaviour */                                             \
+        if (!uv__is_closing((handle)) && uv__is_active((handle)))             \
+          block                                                               \
+        /* Free allocated data */                                             \
+        uv__free(event);                                                      \
+      }                                                                       \
+      if (err != 0 && !uv__is_closing((handle)) && uv__is_active((handle)))   \
+        (handle)->cb((handle), NULL, 0, err);                                 \
+    } while (0)
+
+
+/* Runs in UV loop's thread, when there're events to report to handle */
+static void uv__fsevents_cb(uv_async_t* cb) {
+  uv_fs_event_t* handle;
+
+  handle = (uv_fs_event_t*)cb->data;
+
+  UV__FSEVENTS_PROCESS(handle, {
+    handle->cb(handle, event->path[0] ? event->path : NULL, event->events, 0);
+  });
+}
+
+
+/* Runs in CF thread, pushed event into handle's event list */
+static void uv__fsevents_push_event(uv_fs_event_t* handle,
+                                    QUEUE* events,
+                                    int err) {
+  assert(events != NULL || err != 0);
+  uv_mutex_lock(&handle->cf_mutex);
+
+  /* Concatenate two queues */
+  if (events != NULL)
+    QUEUE_ADD(&handle->cf_events, events);
+
+  /* Propagate error */
+  if (err != 0)
+    handle->cf_error = err;
+  uv_mutex_unlock(&handle->cf_mutex);
+
+  uv_async_send(handle->cf_cb);
+}
+
+
+/* Runs in CF thread, when there're events in FSEventStream */
+static void uv__fsevents_event_cb(const FSEventStreamRef streamRef,
+                                  void* info,
+                                  size_t numEvents,
+                                  void* eventPaths,
+                                  const FSEventStreamEventFlags eventFlags[],
+                                  const FSEventStreamEventId eventIds[]) {
+  size_t i;
+  int len;
+  char** paths;
+  char* path;
+  char* pos;
+  uv_fs_event_t* handle;
+  QUEUE* q;
+  uv_loop_t* loop;
+  uv__cf_loop_state_t* state;
+  uv__fsevents_event_t* event;
+  FSEventStreamEventFlags flags;
+  QUEUE head;
+
+  loop = (uv_loop_t*)info;
+  state = (uv__cf_loop_state_t*)loop->cf_state;
+  assert(state != NULL);
+  paths = (char**)eventPaths;
+
+  /* For each handle */
+  uv_mutex_lock(&state->fsevent_mutex);
+  QUEUE_FOREACH(q, &state->fsevent_handles) {
+    handle = QUEUE_DATA(q, uv_fs_event_t, cf_member);
+    QUEUE_INIT(&head);
+
+    /* Process and filter out events */
+    for (i = 0; i < numEvents; i++) {
+      flags = eventFlags[i];
+
+      /* Ignore system events */
+      if (flags & kFSEventsSystem)
+        continue;
+
+      path = paths[i];
+      len = strlen(path);
+
+      if (handle->realpath_len == 0)
+        continue; /* This should be unreachable */
+
+      /* Filter out paths that are outside handle's request */
+      if (len < handle->realpath_len)
+        continue;
+
+      /* Make sure that realpath actually named a directory,
+       * (unless watching root, which alone keeps a trailing slash on the realpath)
+       * or that we matched the whole string */
+      if (handle->realpath_len != len &&
+          handle->realpath_len > 1 &&
+          path[handle->realpath_len] != '/')
+        continue;
+
+      if (memcmp(path, handle->realpath, handle->realpath_len) != 0)
+        continue;
+
+      if (!(handle->realpath_len == 1 && handle->realpath[0] == '/')) {
+        /* Remove common prefix, unless the watched folder is "/" */
+        path += handle->realpath_len;
+        len -= handle->realpath_len;
+
+        /* Ignore events with path equal to directory itself */
+        if (len <= 1 && (flags & kFSEventStreamEventFlagItemIsDir))
+          continue;
+
+        if (len == 0) {
+          /* Since we're using fsevents to watch the file itself,
+           * realpath == path, and we now need to get the basename of the file back
+           * (for commonality with other codepaths and platforms). */
+          while (len < handle->realpath_len && path[-1] != '/') {
+            path--;
+            len++;
+          }
+          /* Created and Removed seem to be always set, but don't make sense */
+          flags &= ~kFSEventsRenamed;
+        } else {
+          /* Skip forward slash */
+          path++;
+          len--;
+        }
+      }
+
+      /* Do not emit events from subdirectories (without option set) */
+      if ((handle->cf_flags & UV_FS_EVENT_RECURSIVE) == 0 && *path != '\0') {
+        pos = strchr(path + 1, '/');
+        if (pos != NULL)
+          continue;
+      }
+
+      event = (uv__fsevents_event_t*)uv__malloc(sizeof(*event) + len);
+      if (event == NULL)
+        break;
+
+      memset(event, 0, sizeof(*event));
+      memcpy(event->path, path, len + 1);
+      event->events = UV_RENAME;
+
+      if (0 == (flags & kFSEventsRenamed)) {
+        if (0 != (flags & kFSEventsModified) ||
+            0 == (flags & kFSEventStreamEventFlagItemIsDir))
+          event->events = UV_CHANGE;
+      }
+
+      QUEUE_INSERT_TAIL(&head, &event->member);
+    }
+
+    if (!QUEUE_EMPTY(&head))
+      uv__fsevents_push_event(handle, &head, 0);
+  }
+  uv_mutex_unlock(&state->fsevent_mutex);
+}
+
+
+/* Runs in CF thread */
+static int uv__fsevents_create_stream(uv_loop_t* loop, CFArrayRef paths) {
+  uv__cf_loop_state_t* state;
+  FSEventStreamContext ctx;
+  FSEventStreamRef ref;
+  CFAbsoluteTime latency;
+  FSEventStreamCreateFlags flags;
+
+  /* Initialize context */
+  memset(&ctx, 0, sizeof(ctx));
+  ctx.info = loop;
+
+  latency = 0.05;
+
+  /* Explanation of selected flags:
+   * 1. NoDefer - without this flag, events that are happening continuously
+   *    (i.e. each event is happening after time interval less than `latency`,
+   *    counted from previous event), will be deferred and passed to callback
+   *    once they'll either fill whole OS buffer, or when this continuous stream
+   *    will stop (i.e. there'll be delay between events, bigger than
+   *    `latency`).
+   *    Specifying this flag will invoke callback after `latency` time passed
+   *    since event.
+   * 2. FileEvents - fire callback for file changes too (by default it is firing
+   *    it only for directory changes).
+   */
+  flags = kFSEventStreamCreateFlagNoDefer | kFSEventStreamCreateFlagFileEvents;
+
+  /*
+   * NOTE: It might sound like a good idea to remember last seen StreamEventId,
+   * but in reality one dir might have last StreamEventId less than, the other,
+   * that is being watched now. Which will cause FSEventStream API to report
+   * changes to files from the past.
+   */
+  ref = pFSEventStreamCreate(NULL,
+                             &uv__fsevents_event_cb,
+                             &ctx,
+                             paths,
+                             kFSEventStreamEventIdSinceNow,
+                             latency,
+                             flags);
+  assert(ref != NULL);
+
+  state = (uv__cf_loop_state_t*)loop->cf_state;
+  pFSEventStreamScheduleWithRunLoop(ref,
+                                    state->loop,
+                                    *pkCFRunLoopDefaultMode);
+  if (!pFSEventStreamStart(ref)) {
+    pFSEventStreamInvalidate(ref);
+    pFSEventStreamRelease(ref);
+    return UV_EMFILE;
+  }
+
+  state->fsevent_stream = ref;
+  return 0;
+}
+
+
+/* Runs in CF thread */
+static void uv__fsevents_destroy_stream(uv_loop_t* loop) {
+  uv__cf_loop_state_t* state;
+
+  state = (uv__cf_loop_state_t*)loop->cf_state;
+
+  if (state->fsevent_stream == NULL)
+    return;
+
+  /* Stop emitting events */
+  pFSEventStreamStop(state->fsevent_stream);
+
+  /* Release stream */
+  pFSEventStreamInvalidate(state->fsevent_stream);
+  pFSEventStreamRelease(state->fsevent_stream);
+  state->fsevent_stream = NULL;
+}
+
+
+/* Runs in CF thread, when there're new fsevent handles to add to stream */
+static void uv__fsevents_reschedule(uv_fs_event_t* handle,
+                                    uv__cf_loop_signal_type_t type) {
+  uv__cf_loop_state_t* state;
+  QUEUE* q;
+  uv_fs_event_t* curr;
+  CFArrayRef cf_paths;
+  CFStringRef* paths;
+  unsigned int i;
+  int err;
+  unsigned int path_count;
+
+  state = (uv__cf_loop_state_t*)handle->loop->cf_state;
+  paths = NULL;
+  cf_paths = NULL;
+  err = 0;
+  /* NOTE: `i` is used in deallocation loop below */
+  i = 0;
+
+  /* Optimization to prevent O(n^2) time spent when starting to watch
+   * many files simultaneously
+   */
+  uv_mutex_lock(&state->fsevent_mutex);
+  if (state->fsevent_need_reschedule == 0) {
+    uv_mutex_unlock(&state->fsevent_mutex);
+    goto final;
+  }
+  state->fsevent_need_reschedule = 0;
+  uv_mutex_unlock(&state->fsevent_mutex);
+
+  /* Destroy previous FSEventStream */
+  uv__fsevents_destroy_stream(handle->loop);
+
+  /* Any failure below will be a memory failure */
+  err = UV_ENOMEM;
+
+  /* Create list of all watched paths */
+  uv_mutex_lock(&state->fsevent_mutex);
+  path_count = state->fsevent_handle_count;
+  if (path_count != 0) {
+    paths = (CFStringRef*)uv__malloc(sizeof(*paths) * path_count);
+    if (paths == NULL) {
+      uv_mutex_unlock(&state->fsevent_mutex);
+      goto final;
+    }
+
+    q = &state->fsevent_handles;
+    for (; i < path_count; i++) {
+      q = QUEUE_NEXT(q);
+      assert(q != &state->fsevent_handles);
+      curr = QUEUE_DATA(q, uv_fs_event_t, cf_member);
+
+      assert(curr->realpath != NULL);
+      paths[i] =
+          pCFStringCreateWithFileSystemRepresentation(NULL, curr->realpath);
+      if (paths[i] == NULL) {
+        uv_mutex_unlock(&state->fsevent_mutex);
+        goto final;
+      }
+    }
+  }
+  uv_mutex_unlock(&state->fsevent_mutex);
+  err = 0;
+
+  if (path_count != 0) {
+    /* Create new FSEventStream */
+    cf_paths = pCFArrayCreate(NULL, (const void**) paths, path_count, NULL);
+    if (cf_paths == NULL) {
+      err = UV_ENOMEM;
+      goto final;
+    }
+    err = uv__fsevents_create_stream(handle->loop, cf_paths);
+  }
+
+final:
+  /* Deallocate all paths in case of failure */
+  if (err != 0) {
+    if (cf_paths == NULL) {
+      while (i != 0)
+        pCFRelease(paths[--i]);
+      uv__free(paths);
+    } else {
+      /* CFArray takes ownership of both strings and original C-array */
+      pCFRelease(cf_paths);
+    }
+
+    /* Broadcast error to all handles */
+    uv_mutex_lock(&state->fsevent_mutex);
+    QUEUE_FOREACH(q, &state->fsevent_handles) {
+      curr = QUEUE_DATA(q, uv_fs_event_t, cf_member);
+      uv__fsevents_push_event(curr, NULL, err);
+    }
+    uv_mutex_unlock(&state->fsevent_mutex);
+  }
+
+  /*
+   * Main thread will block until the removal of handle from the list,
+   * we must tell it when we're ready.
+   *
+   * NOTE: This is coupled with `uv_sem_wait()` in `uv__fsevents_close`
+   */
+  if (type == kUVCFLoopSignalClosing)
+    uv_sem_post(&state->fsevent_sem);
+}
+
+
+static int uv__fsevents_global_init(void) {
+  static pthread_mutex_t global_init_mutex = PTHREAD_MUTEX_INITIALIZER;
+  static void* core_foundation_handle;
+  static void* core_services_handle;
+  int err;
+
+  err = 0;
+  pthread_mutex_lock(&global_init_mutex);
+  if (core_foundation_handle != NULL)
+    goto out;
+
+  /* The libraries are never unloaded because we currently don't have a good
+   * mechanism for keeping a reference count. It's unlikely to be an issue
+   * but if it ever becomes one, we can turn the dynamic library handles into
+   * per-event loop properties and have the dynamic linker keep track for us.
+   */
+  err = UV_ENOSYS;
+  core_foundation_handle = dlopen("/System/Library/Frameworks/"
+                                  "CoreFoundation.framework/"
+                                  "Versions/A/CoreFoundation",
+                                  RTLD_LAZY | RTLD_LOCAL);
+  if (core_foundation_handle == NULL)
+    goto out;
+
+  core_services_handle = dlopen("/System/Library/Frameworks/"
+                                "CoreServices.framework/"
+                                "Versions/A/CoreServices",
+                                RTLD_LAZY | RTLD_LOCAL);
+  if (core_services_handle == NULL)
+    goto out;
+
+  err = UV_ENOENT;
+#define V(handle, symbol)                                                     \
+  do {                                                                        \
+    *(void **)(&p ## symbol) = dlsym((handle), #symbol);                      \
+    if (p ## symbol == NULL)                                                  \
+      goto out;                                                               \
+  }                                                                           \
+  while (0)
+  V(core_foundation_handle, CFArrayCreate);
+  V(core_foundation_handle, CFRelease);
+  V(core_foundation_handle, CFRunLoopAddSource);
+  V(core_foundation_handle, CFRunLoopGetCurrent);
+  V(core_foundation_handle, CFRunLoopRemoveSource);
+  V(core_foundation_handle, CFRunLoopRun);
+  V(core_foundation_handle, CFRunLoopSourceCreate);
+  V(core_foundation_handle, CFRunLoopSourceSignal);
+  V(core_foundation_handle, CFRunLoopStop);
+  V(core_foundation_handle, CFRunLoopWakeUp);
+  V(core_foundation_handle, CFStringCreateWithFileSystemRepresentation);
+  V(core_foundation_handle, CFStringGetSystemEncoding);
+  V(core_foundation_handle, kCFRunLoopDefaultMode);
+  V(core_services_handle, FSEventStreamCreate);
+  V(core_services_handle, FSEventStreamFlushSync);
+  V(core_services_handle, FSEventStreamInvalidate);
+  V(core_services_handle, FSEventStreamRelease);
+  V(core_services_handle, FSEventStreamScheduleWithRunLoop);
+  V(core_services_handle, FSEventStreamStart);
+  V(core_services_handle, FSEventStreamStop);
+#undef V
+  err = 0;
+
+out:
+  if (err && core_services_handle != NULL) {
+    dlclose(core_services_handle);
+    core_services_handle = NULL;
+  }
+
+  if (err && core_foundation_handle != NULL) {
+    dlclose(core_foundation_handle);
+    core_foundation_handle = NULL;
+  }
+
+  pthread_mutex_unlock(&global_init_mutex);
+  return err;
+}
+
+
+/* Runs in UV loop */
+static int uv__fsevents_loop_init(uv_loop_t* loop) {
+  CFRunLoopSourceContext ctx;
+  uv__cf_loop_state_t* state;
+  pthread_attr_t attr;
+  int err;
+
+  if (loop->cf_state != NULL)
+    return 0;
+
+  err = uv__fsevents_global_init();
+  if (err)
+    return err;
+
+  state = (uv__cf_loop_state_t*)uv__calloc(1, sizeof(*state));
+  if (state == NULL)
+    return UV_ENOMEM;
+
+  err = uv_mutex_init(&loop->cf_mutex);
+  if (err)
+    goto fail_mutex_init;
+
+  err = uv_sem_init(&loop->cf_sem, 0);
+  if (err)
+    goto fail_sem_init;
+
+  QUEUE_INIT(&loop->cf_signals);
+
+  err = uv_sem_init(&state->fsevent_sem, 0);
+  if (err)
+    goto fail_fsevent_sem_init;
+
+  err = uv_mutex_init(&state->fsevent_mutex);
+  if (err)
+    goto fail_fsevent_mutex_init;
+
+  QUEUE_INIT(&state->fsevent_handles);
+  state->fsevent_need_reschedule = 0;
+  state->fsevent_handle_count = 0;
+
+  memset(&ctx, 0, sizeof(ctx));
+  ctx.info = loop;
+  ctx.perform = uv__cf_loop_cb;
+  state->signal_source = pCFRunLoopSourceCreate(NULL, 0, &ctx);
+  if (state->signal_source == NULL) {
+    err = UV_ENOMEM;
+    goto fail_signal_source_create;
+  }
+
+  if (pthread_attr_init(&attr))
+    abort();
+
+  if (pthread_attr_setstacksize(&attr, uv__thread_stack_size()))
+    abort();
+
+  loop->cf_state = state;
+
+  /* uv_thread_t is an alias for pthread_t. */
+  err = UV__ERR(pthread_create(&loop->cf_thread, &attr, uv__cf_loop_runner, loop));
+
+  if (pthread_attr_destroy(&attr))
+    abort();
+
+  if (err)
+    goto fail_thread_create;
+
+  /* Synchronize threads */
+  uv_sem_wait(&loop->cf_sem);
+  return 0;
+
+fail_thread_create:
+  loop->cf_state = NULL;
+
+fail_signal_source_create:
+  uv_mutex_destroy(&state->fsevent_mutex);
+
+fail_fsevent_mutex_init:
+  uv_sem_destroy(&state->fsevent_sem);
+
+fail_fsevent_sem_init:
+  uv_sem_destroy(&loop->cf_sem);
+
+fail_sem_init:
+  uv_mutex_destroy(&loop->cf_mutex);
+
+fail_mutex_init:
+  uv__free(state);
+  return err;
+}
+
+
+/* Runs in UV loop */
+void uv__fsevents_loop_delete(uv_loop_t* loop) {
+  uv__cf_loop_signal_t* s;
+  uv__cf_loop_state_t* state;
+  QUEUE* q;
+
+  if (loop->cf_state == NULL)
+    return;
+
+  if (uv__cf_loop_signal(loop, NULL, kUVCFLoopSignalRegular) != 0)
+    abort();
+
+  uv_thread_join(&loop->cf_thread);
+  uv_sem_destroy(&loop->cf_sem);
+  uv_mutex_destroy(&loop->cf_mutex);
+
+  /* Free any remaining data */
+  while (!QUEUE_EMPTY(&loop->cf_signals)) {
+    q = QUEUE_HEAD(&loop->cf_signals);
+    s = QUEUE_DATA(q, uv__cf_loop_signal_t, member);
+    QUEUE_REMOVE(q);
+    uv__free(s);
+  }
+
+  /* Destroy state */
+  state = (uv__cf_loop_state_t*)loop->cf_state;
+  uv_sem_destroy(&state->fsevent_sem);
+  uv_mutex_destroy(&state->fsevent_mutex);
+  pCFRelease(state->signal_source);
+  uv__free(state);
+  loop->cf_state = NULL;
+}
+
+
+/* Runs in CF thread. This is the CF loop's body */
+static void* uv__cf_loop_runner(void* arg) {
+  uv_loop_t* loop;
+  uv__cf_loop_state_t* state;
+
+  loop = (uv_loop_t*)arg;
+  state = (uv__cf_loop_state_t*)loop->cf_state;
+  state->loop = pCFRunLoopGetCurrent();
+
+  pCFRunLoopAddSource(state->loop,
+                      state->signal_source,
+                      *pkCFRunLoopDefaultMode);
+
+  uv_sem_post(&loop->cf_sem);
+
+  pCFRunLoopRun();
+  pCFRunLoopRemoveSource(state->loop,
+                         state->signal_source,
+                         *pkCFRunLoopDefaultMode);
+
+  state->loop = NULL;
+
+  return NULL;
+}
+
+
+/* Runs in CF thread, executed after `uv__cf_loop_signal()` */
+static void uv__cf_loop_cb(void* arg) {
+  uv_loop_t* loop;
+  uv__cf_loop_state_t* state;
+  QUEUE* item;
+  QUEUE split_head;
+  uv__cf_loop_signal_t* s;
+
+  loop = (uv_loop_t*)arg;
+  state = (uv__cf_loop_state_t*)loop->cf_state;
+
+  uv_mutex_lock(&loop->cf_mutex);
+  QUEUE_MOVE(&loop->cf_signals, &split_head);
+  uv_mutex_unlock(&loop->cf_mutex);
+
+  while (!QUEUE_EMPTY(&split_head)) {
+    item = QUEUE_HEAD(&split_head);
+    QUEUE_REMOVE(item);
+
+    s = QUEUE_DATA(item, uv__cf_loop_signal_t, member);
+
+    /* This was a termination signal */
+    if (s->handle == NULL)
+      pCFRunLoopStop(state->loop);
+    else
+      uv__fsevents_reschedule(s->handle, s->type);
+
+    uv__free(s);
+  }
+}
+
+
+/* Runs in UV loop to notify CF thread */
+int uv__cf_loop_signal(uv_loop_t* loop,
+                       uv_fs_event_t* handle,
+                       uv__cf_loop_signal_type_t type) {
+  uv__cf_loop_signal_t* item;
+  uv__cf_loop_state_t* state;
+
+  item = (uv__cf_loop_signal_t*)uv__malloc(sizeof(*item));
+  if (item == NULL)
+    return UV_ENOMEM;
+
+  item->handle = handle;
+  item->type = type;
+
+  uv_mutex_lock(&loop->cf_mutex);
+  QUEUE_INSERT_TAIL(&loop->cf_signals, &item->member);
+
+  state = (uv__cf_loop_state_t*)loop->cf_state;
+  assert(state != NULL);
+  pCFRunLoopSourceSignal(state->signal_source);
+  pCFRunLoopWakeUp(state->loop);
+
+  uv_mutex_unlock(&loop->cf_mutex);
+
+  return 0;
+}
+
+
+/* Runs in UV loop to initialize handle */
+int uv__fsevents_init(uv_fs_event_t* handle) {
+  int err;
+  uv__cf_loop_state_t* state;
+
+  err = uv__fsevents_loop_init(handle->loop);
+  if (err)
+    return err;
+
+  /* Get absolute path to file */
+  handle->realpath = realpath(handle->path, NULL);
+  if (handle->realpath == NULL)
+    return UV__ERR(errno);
+  handle->realpath_len = strlen(handle->realpath);
+
+  /* Initialize event queue */
+  QUEUE_INIT(&handle->cf_events);
+  handle->cf_error = 0;
+
+  /*
+   * Events will occur in other thread.
+   * Initialize callback for getting them back into event loop's thread
+   */
+  handle->cf_cb = (uv_async_t*)uv__malloc(sizeof(*handle->cf_cb));
+  if (handle->cf_cb == NULL) {
+    err = UV_ENOMEM;
+    goto fail_cf_cb_malloc;
+  }
+
+  handle->cf_cb->data = handle;
+  uv_async_init(handle->loop, handle->cf_cb, uv__fsevents_cb);
+  handle->cf_cb->flags |= UV_HANDLE_INTERNAL;
+  uv_unref((uv_handle_t*) handle->cf_cb);
+
+  err = uv_mutex_init(&handle->cf_mutex);
+  if (err)
+    goto fail_cf_mutex_init;
+
+  /* Insert handle into the list */
+  state = (uv__cf_loop_state_t*)handle->loop->cf_state;
+  uv_mutex_lock(&state->fsevent_mutex);
+  QUEUE_INSERT_TAIL(&state->fsevent_handles, &handle->cf_member);
+  state->fsevent_handle_count++;
+  state->fsevent_need_reschedule = 1;
+  uv_mutex_unlock(&state->fsevent_mutex);
+
+  /* Reschedule FSEventStream */
+  assert(handle != NULL);
+  err = uv__cf_loop_signal(handle->loop, handle, kUVCFLoopSignalRegular);
+  if (err)
+    goto fail_loop_signal;
+
+  return 0;
+
+fail_loop_signal:
+  uv_mutex_destroy(&handle->cf_mutex);
+
+fail_cf_mutex_init:
+  uv__free(handle->cf_cb);
+  handle->cf_cb = NULL;
+
+fail_cf_cb_malloc:
+  uv__free(handle->realpath);
+  handle->realpath = NULL;
+  handle->realpath_len = 0;
+
+  return err;
+}
+
+
+/* Runs in UV loop to de-initialize handle */
+int uv__fsevents_close(uv_fs_event_t* handle) {
+  int err;
+  uv__cf_loop_state_t* state;
+
+  if (handle->cf_cb == NULL)
+    return UV_EINVAL;
+
+  /* Remove handle from  the list */
+  state = (uv__cf_loop_state_t*)handle->loop->cf_state;
+  uv_mutex_lock(&state->fsevent_mutex);
+  QUEUE_REMOVE(&handle->cf_member);
+  state->fsevent_handle_count--;
+  state->fsevent_need_reschedule = 1;
+  uv_mutex_unlock(&state->fsevent_mutex);
+
+  /* Reschedule FSEventStream */
+  assert(handle != NULL);
+  err = uv__cf_loop_signal(handle->loop, handle, kUVCFLoopSignalClosing);
+  if (err)
+    return UV__ERR(err);
+
+  /* Wait for deinitialization */
+  uv_sem_wait(&state->fsevent_sem);
+
+  uv_close((uv_handle_t*) handle->cf_cb, (uv_close_cb) uv__free);
+  handle->cf_cb = NULL;
+
+  /* Free data in queue */
+  UV__FSEVENTS_PROCESS(handle, {
+    /* NOP */
+  });
+
+  uv_mutex_destroy(&handle->cf_mutex);
+  uv__free(handle->realpath);
+  handle->realpath = NULL;
+  handle->realpath_len = 0;
+
+  return 0;
+}
+
+#endif /* TARGET_OS_IPHONE */
diff --git a/wpinet/src/main/native/thirdparty/libuv/src/unix/getaddrinfo.cpp b/wpinet/src/main/native/thirdparty/libuv/src/unix/getaddrinfo.cpp
new file mode 100644
index 0000000..41dc390
--- /dev/null
+++ b/wpinet/src/main/native/thirdparty/libuv/src/unix/getaddrinfo.cpp
@@ -0,0 +1,252 @@
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+/* Expose glibc-specific EAI_* error codes. Needs to be defined before we
+ * include any headers.
+ */
+
+#include "uv.h"
+#include "internal.h"
+#include "idna.h"
+
+#include <errno.h>
+#include <stddef.h> /* NULL */
+#include <stdlib.h>
+#include <string.h>
+#include <net/if.h> /* if_indextoname() */
+
+/* EAI_* constants. */
+#include <netdb.h>
+
+
+int uv__getaddrinfo_translate_error(int sys_err) {
+  switch (sys_err) {
+  case 0: return 0;
+#if defined(EAI_ADDRFAMILY)
+  case EAI_ADDRFAMILY: return UV_EAI_ADDRFAMILY;
+#endif
+#if defined(EAI_AGAIN)
+  case EAI_AGAIN: return UV_EAI_AGAIN;
+#endif
+#if defined(EAI_BADFLAGS)
+  case EAI_BADFLAGS: return UV_EAI_BADFLAGS;
+#endif
+#if defined(EAI_BADHINTS)
+  case EAI_BADHINTS: return UV_EAI_BADHINTS;
+#endif
+#if defined(EAI_CANCELED)
+  case EAI_CANCELED: return UV_EAI_CANCELED;
+#endif
+#if defined(EAI_FAIL)
+  case EAI_FAIL: return UV_EAI_FAIL;
+#endif
+#if defined(EAI_FAMILY)
+  case EAI_FAMILY: return UV_EAI_FAMILY;
+#endif
+#if defined(EAI_MEMORY)
+  case EAI_MEMORY: return UV_EAI_MEMORY;
+#endif
+#if defined(EAI_NODATA)
+  case EAI_NODATA: return UV_EAI_NODATA;
+#endif
+#if defined(EAI_NONAME)
+# if !defined(EAI_NODATA) || EAI_NODATA != EAI_NONAME
+  case EAI_NONAME: return UV_EAI_NONAME;
+# endif
+#endif
+#if defined(EAI_OVERFLOW)
+  case EAI_OVERFLOW: return UV_EAI_OVERFLOW;
+#endif
+#if defined(EAI_PROTOCOL)
+  case EAI_PROTOCOL: return UV_EAI_PROTOCOL;
+#endif
+#if defined(EAI_SERVICE)
+  case EAI_SERVICE: return UV_EAI_SERVICE;
+#endif
+#if defined(EAI_SOCKTYPE)
+  case EAI_SOCKTYPE: return UV_EAI_SOCKTYPE;
+#endif
+#if defined(EAI_SYSTEM)
+  case EAI_SYSTEM: return UV__ERR(errno);
+#endif
+  }
+  assert(!"unknown EAI_* error code");
+  abort();
+#ifndef __SUNPRO_C
+  return 0;  /* Pacify compiler. */
+#endif
+}
+
+
+static void uv__getaddrinfo_work(struct uv__work* w) {
+  uv_getaddrinfo_t* req;
+  int err;
+
+  req = container_of(w, uv_getaddrinfo_t, work_req);
+  err = getaddrinfo(req->hostname, req->service, req->hints, &req->addrinfo);
+  req->retcode = uv__getaddrinfo_translate_error(err);
+}
+
+
+static void uv__getaddrinfo_done(struct uv__work* w, int status) {
+  uv_getaddrinfo_t* req;
+
+  req = container_of(w, uv_getaddrinfo_t, work_req);
+  uv__req_unregister(req->loop, req);
+
+  /* See initialization in uv_getaddrinfo(). */
+  if (req->hints)
+    uv__free(req->hints);
+  else if (req->service)
+    uv__free(req->service);
+  else if (req->hostname)
+    uv__free(req->hostname);
+  else
+    assert(0);
+
+  req->hints = NULL;
+  req->service = NULL;
+  req->hostname = NULL;
+
+  if (status == UV_ECANCELED) {
+    assert(req->retcode == 0);
+    req->retcode = UV_EAI_CANCELED;
+  }
+
+  if (req->cb)
+    req->cb(req, req->retcode, req->addrinfo);
+}
+
+
+int uv_getaddrinfo(uv_loop_t* loop,
+                   uv_getaddrinfo_t* req,
+                   uv_getaddrinfo_cb cb,
+                   const char* hostname,
+                   const char* service,
+                   const struct addrinfo* hints) {
+  char hostname_ascii[256];
+  size_t hostname_len;
+  size_t service_len;
+  size_t hints_len;
+  size_t len;
+  char* buf;
+  long rc;
+
+  if (req == NULL || (hostname == NULL && service == NULL))
+    return UV_EINVAL;
+
+  /* FIXME(bnoordhuis) IDNA does not seem to work z/OS,
+   * probably because it uses EBCDIC rather than ASCII.
+   */
+#ifdef __MVS__
+  (void) &hostname_ascii;
+#else
+  if (hostname != NULL) {
+    rc = uv__idna_toascii(hostname,
+                          hostname + strlen(hostname),
+                          hostname_ascii,
+                          hostname_ascii + sizeof(hostname_ascii));
+    if (rc < 0)
+      return rc;
+    hostname = hostname_ascii;
+  }
+#endif
+
+  hostname_len = hostname ? strlen(hostname) + 1 : 0;
+  service_len = service ? strlen(service) + 1 : 0;
+  hints_len = hints ? sizeof(*hints) : 0;
+  buf = (char*)uv__malloc(hostname_len + service_len + hints_len);
+
+  if (buf == NULL)
+    return UV_ENOMEM;
+
+  uv__req_init(loop, req, UV_GETADDRINFO);
+  req->loop = loop;
+  req->cb = cb;
+  req->addrinfo = NULL;
+  req->hints = NULL;
+  req->service = NULL;
+  req->hostname = NULL;
+  req->retcode = 0;
+
+  /* order matters, see uv_getaddrinfo_done() */
+  len = 0;
+
+  if (hints) {
+    req->hints = (struct addrinfo*)memcpy(buf + len, hints, sizeof(*hints));
+    len += sizeof(*hints);
+  }
+
+  if (service) {
+    req->service = (char*)memcpy(buf + len, service, service_len);
+    len += service_len;
+  }
+
+  if (hostname)
+    req->hostname = (char*)memcpy(buf + len, hostname, hostname_len);
+
+  if (cb) {
+    uv__work_submit(loop,
+                    &req->work_req,
+                    UV__WORK_SLOW_IO,
+                    uv__getaddrinfo_work,
+                    uv__getaddrinfo_done);
+    return 0;
+  } else {
+    uv__getaddrinfo_work(&req->work_req);
+    uv__getaddrinfo_done(&req->work_req, 0);
+    return req->retcode;
+  }
+}
+
+
+void uv_freeaddrinfo(struct addrinfo* ai) {
+  if (ai)
+    freeaddrinfo(ai);
+}
+
+
+int uv_if_indextoname(unsigned int ifindex, char* buffer, size_t* size) {
+  char ifname_buf[UV_IF_NAMESIZE];
+  size_t len;
+
+  if (buffer == NULL || size == NULL || *size == 0)
+    return UV_EINVAL;
+
+  if (if_indextoname(ifindex, ifname_buf) == NULL)
+    return UV__ERR(errno);
+
+  len = strnlen(ifname_buf, sizeof(ifname_buf));
+
+  if (*size <= len) {
+    *size = len + 1;
+    return UV_ENOBUFS;
+  }
+
+  memcpy(buffer, ifname_buf, len);
+  buffer[len] = '\0';
+  *size = len;
+
+  return 0;
+}
+
+int uv_if_indextoiid(unsigned int ifindex, char* buffer, size_t* size) {
+  return uv_if_indextoname(ifindex, buffer, size);
+}
diff --git a/wpinet/src/main/native/thirdparty/libuv/src/unix/getnameinfo.cpp b/wpinet/src/main/native/thirdparty/libuv/src/unix/getnameinfo.cpp
new file mode 100644
index 0000000..991002a
--- /dev/null
+++ b/wpinet/src/main/native/thirdparty/libuv/src/unix/getnameinfo.cpp
@@ -0,0 +1,121 @@
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+*
+* Permission is hereby granted, free of charge, to any person obtaining a copy
+* of this software and associated documentation files (the "Software"), to
+* deal in the Software without restriction, including without limitation the
+* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+* sell copies of the Software, and to permit persons to whom the Software is
+* furnished to do so, subject to the following conditions:
+*
+* The above copyright notice and this permission notice shall be included in
+* all copies or substantial portions of the Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+* IN THE SOFTWARE.
+*/
+
+#include <assert.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "uv.h"
+#include "internal.h"
+
+
+static void uv__getnameinfo_work(struct uv__work* w) {
+  uv_getnameinfo_t* req;
+  int err;
+  socklen_t salen;
+
+  req = container_of(w, uv_getnameinfo_t, work_req);
+
+  if (req->storage.ss_family == AF_INET)
+    salen = sizeof(struct sockaddr_in);
+  else if (req->storage.ss_family == AF_INET6)
+    salen = sizeof(struct sockaddr_in6);
+  else
+    abort();
+
+  err = getnameinfo((struct sockaddr*) &req->storage,
+                    salen,
+                    req->host,
+                    sizeof(req->host),
+                    req->service,
+                    sizeof(req->service),
+                    req->flags);
+  req->retcode = uv__getaddrinfo_translate_error(err);
+}
+
+static void uv__getnameinfo_done(struct uv__work* w, int status) {
+  uv_getnameinfo_t* req;
+  char* host;
+  char* service;
+
+  req = container_of(w, uv_getnameinfo_t, work_req);
+  uv__req_unregister(req->loop, req);
+  host = service = NULL;
+
+  if (status == UV_ECANCELED) {
+    assert(req->retcode == 0);
+    req->retcode = UV_EAI_CANCELED;
+  } else if (req->retcode == 0) {
+    host = req->host;
+    service = req->service;
+  }
+
+  if (req->getnameinfo_cb)
+    req->getnameinfo_cb(req, req->retcode, host, service);
+}
+
+/*
+* Entry point for getnameinfo
+* return 0 if a callback will be made
+* return error code if validation fails
+*/
+int uv_getnameinfo(uv_loop_t* loop,
+                   uv_getnameinfo_t* req,
+                   uv_getnameinfo_cb getnameinfo_cb,
+                   const struct sockaddr* addr,
+                   int flags) {
+  if (req == NULL || addr == NULL)
+    return UV_EINVAL;
+
+  if (addr->sa_family == AF_INET) {
+    memcpy(&req->storage,
+           addr,
+           sizeof(struct sockaddr_in));
+  } else if (addr->sa_family == AF_INET6) {
+    memcpy(&req->storage,
+           addr,
+           sizeof(struct sockaddr_in6));
+  } else {
+    return UV_EINVAL;
+  }
+
+  uv__req_init(loop, (uv_req_t*)req, UV_GETNAMEINFO);
+
+  req->getnameinfo_cb = getnameinfo_cb;
+  req->flags = flags;
+  req->type = UV_GETNAMEINFO;
+  req->loop = loop;
+  req->retcode = 0;
+
+  if (getnameinfo_cb) {
+    uv__work_submit(loop,
+                    &req->work_req,
+                    UV__WORK_SLOW_IO,
+                    uv__getnameinfo_work,
+                    uv__getnameinfo_done);
+    return 0;
+  } else {
+    uv__getnameinfo_work(&req->work_req);
+    uv__getnameinfo_done(&req->work_req, 0);
+    return req->retcode;
+  }
+}
diff --git a/wpinet/src/main/native/thirdparty/libuv/src/unix/ibmi.cpp b/wpinet/src/main/native/thirdparty/libuv/src/unix/ibmi.cpp
new file mode 100644
index 0000000..56af31e
--- /dev/null
+++ b/wpinet/src/main/native/thirdparty/libuv/src/unix/ibmi.cpp
@@ -0,0 +1,537 @@
+/* Copyright libuv project contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include "uv.h"
+#include "internal.h"
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <sys/time.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <utmp.h>
+#include <libgen.h>
+
+#include <sys/protosw.h>
+#include <procinfo.h>
+#include <sys/proc.h>
+#include <sys/procfs.h>
+
+#include <ctype.h>
+
+#include <sys/mntctl.h>
+#include <sys/vmount.h>
+#include <limits.h>
+#include <strings.h>
+#include <sys/vnode.h>
+
+#include <as400_protos.h>
+#include <as400_types.h>
+
+char* original_exepath = NULL;
+uv_mutex_t process_title_mutex;
+uv_once_t process_title_mutex_once = UV_ONCE_INIT;
+
+typedef struct {
+  int bytes_available;
+  int bytes_returned;
+  char current_date_and_time[8];
+  char system_name[8];
+  char elapsed_time[6];
+  char restricted_state_flag;
+  char reserved;
+  int percent_processing_unit_used;
+  int jobs_in_system;
+  int percent_permanent_addresses;
+  int percent_temporary_addresses;
+  int system_asp;
+  int percent_system_asp_used;
+  int total_auxiliary_storage;
+  int current_unprotected_storage_used;
+  int maximum_unprotected_storage_used;
+  int percent_db_capability;
+  int main_storage_size;
+  int number_of_partitions;
+  int partition_identifier;
+  int reserved1;
+  int current_processing_capacity;
+  char processor_sharing_attribute;
+  char reserved2[3];
+  int number_of_processors;
+  int active_jobs_in_system;
+  int active_threads_in_system;
+  int maximum_jobs_in_system;
+  int percent_temporary_256mb_segments_used;
+  int percent_temporary_4gb_segments_used;
+  int percent_permanent_256mb_segments_used;
+  int percent_permanent_4gb_segments_used;
+  int percent_current_interactive_performance;
+  int percent_uncapped_cpu_capacity_used;
+  int percent_shared_processor_pool_used;
+  long main_storage_size_long;
+} SSTS0200;
+
+
+typedef struct {
+  char header[208];
+  unsigned char loca_adapter_address[12];
+} LIND0500;
+
+
+typedef struct {
+  int bytes_provided;
+  int bytes_available;
+  char msgid[7];
+} errcode_s;
+
+
+static const unsigned char e2a[256] = {
+    0, 1, 2, 3, 156, 9, 134, 127, 151, 141, 142, 11, 12, 13, 14, 15,
+    16, 17, 18, 19, 157, 133, 8, 135, 24, 25, 146, 143, 28, 29, 30, 31,
+    128, 129, 130, 131, 132, 10, 23, 27, 136, 137, 138, 139, 140, 5, 6, 7,
+    144, 145, 22, 147, 148, 149, 150, 4, 152, 153, 154, 155, 20, 21, 158, 26,
+    32, 160, 161, 162, 163, 164, 165, 166, 167, 168, 91, 46, 60, 40, 43, 33,
+    38, 169, 170, 171, 172, 173, 174, 175, 176, 177, 93, 36, 42, 41, 59, 94,
+    45, 47, 178, 179, 180, 181, 182, 183, 184, 185, 124, 44, 37, 95, 62, 63,
+    186, 187, 188, 189, 190, 191, 192, 193, 194, 96, 58, 35, 64, 39, 61, 34,
+    195, 97, 98, 99, 100, 101, 102, 103, 104, 105, 196, 197, 198, 199, 200, 201,
+    202, 106, 107, 108, 109, 110, 111, 112, 113, 114, 203, 204, 205, 206, 207, 208,
+    209, 126, 115, 116, 117, 118, 119, 120, 121, 122, 210, 211, 212, 213, 214, 215,
+    216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231,
+    123, 65, 66, 67, 68, 69, 70, 71, 72, 73, 232, 233, 234, 235, 236, 237,
+    125, 74, 75, 76, 77, 78, 79, 80, 81, 82, 238, 239, 240, 241, 242, 243,
+    92, 159, 83, 84, 85, 86, 87, 88, 89, 90, 244, 245, 246, 247, 248, 249,
+    48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 250, 251, 252, 253, 254, 255};
+
+
+static const unsigned char a2e[256] = {
+    0, 1, 2, 3, 55, 45, 46, 47, 22, 5, 37, 11, 12, 13, 14, 15,
+    16, 17, 18, 19, 60, 61, 50, 38, 24, 25, 63, 39, 28, 29, 30, 31,
+    64, 79, 127, 123, 91, 108, 80, 125, 77, 93, 92, 78, 107, 96, 75, 97,
+    240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 122, 94, 76, 126, 110, 111,
+    124, 193, 194, 195, 196, 197, 198, 199, 200, 201, 209, 210, 211, 212, 213, 214,
+    215, 216, 217, 226, 227, 228, 229, 230, 231, 232, 233, 74, 224, 90, 95, 109,
+    121, 129, 130, 131, 132, 133, 134, 135, 136, 137, 145, 146, 147, 148, 149, 150,
+    151, 152, 153, 162, 163, 164, 165, 166, 167, 168, 169, 192, 106, 208, 161, 7,
+    32, 33, 34, 35, 36, 21, 6, 23, 40, 41, 42, 43, 44, 9, 10, 27,
+    48, 49, 26, 51, 52, 53, 54, 8, 56, 57, 58, 59, 4, 20, 62, 225,
+    65, 66, 67, 68, 69, 70, 71, 72, 73, 81, 82, 83, 84, 85, 86, 87,
+    88, 89, 98, 99, 100, 101, 102, 103, 104, 105, 112, 113, 114, 115, 116, 117,
+    118, 119, 120, 128, 138, 139, 140, 141, 142, 143, 144, 154, 155, 156, 157, 158,
+    159, 160, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183,
+    184, 185, 186, 187, 188, 189, 190, 191, 202, 203, 204, 205, 206, 207, 218, 219,
+    220, 221, 222, 223, 234, 235, 236, 237, 238, 239, 250, 251, 252, 253, 254, 255};
+
+
+static void iconv_e2a(unsigned char src[], unsigned char dst[], size_t length) {
+  size_t i;
+  for (i = 0; i < length; i++)
+    dst[i] = e2a[src[i]];
+}
+
+
+static void iconv_a2e(const char* src, unsigned char dst[], size_t length) {
+  size_t srclen;
+  size_t i;
+
+  srclen = strlen(src);
+  if (srclen > length)
+    srclen = length;
+  for (i = 0; i < srclen; i++)
+    dst[i] = a2e[src[i]];
+  /* padding the remaining part with spaces */
+  for (; i < length; i++)
+    dst[i] = a2e[' '];
+}
+
+void init_process_title_mutex_once(void) {
+  uv_mutex_init(&process_title_mutex);
+}
+
+static int get_ibmi_system_status(SSTS0200* rcvr) {
+  /* rcvrlen is input parameter 2 to QWCRSSTS */
+  unsigned int rcvrlen = sizeof(*rcvr);
+  unsigned char format[8], reset_status[10];
+
+  /* format is input parameter 3 to QWCRSSTS */
+  iconv_a2e("SSTS0200", format, sizeof(format));
+  /* reset_status is input parameter 4 */
+  iconv_a2e("*NO", reset_status, sizeof(reset_status));
+
+  /* errcode is input parameter 5 to QWCRSSTS */
+  errcode_s errcode;
+
+  /* qwcrssts_pointer is the 16-byte tagged system pointer to QWCRSSTS */
+  ILEpointer __attribute__((aligned(16))) qwcrssts_pointer;
+
+  /* qwcrssts_argv is the array of argument pointers to QWCRSSTS */
+  void* qwcrssts_argv[6];
+
+  /* Set the IBM i pointer to the QSYS/QWCRSSTS *PGM object */
+  int rc = _RSLOBJ2(&qwcrssts_pointer, RSLOBJ_TS_PGM, "QWCRSSTS", "QSYS");
+
+  if (rc != 0)
+    return rc;
+
+  /* initialize the QWCRSSTS returned info structure */
+  memset(rcvr, 0, sizeof(*rcvr));
+
+  /* initialize the QWCRSSTS error code structure */
+  memset(&errcode, 0, sizeof(errcode));
+  errcode.bytes_provided = sizeof(errcode);
+
+  /* initialize the array of argument pointers for the QWCRSSTS API */
+  qwcrssts_argv[0] = rcvr;
+  qwcrssts_argv[1] = &rcvrlen;
+  qwcrssts_argv[2] = &format;
+  qwcrssts_argv[3] = &reset_status;
+  qwcrssts_argv[4] = &errcode;
+  qwcrssts_argv[5] = NULL;
+
+  /* Call the IBM i QWCRSSTS API from PASE */
+  rc = _PGMCALL(&qwcrssts_pointer, qwcrssts_argv, 0);
+
+  return rc;
+}
+
+
+uint64_t uv_get_free_memory(void) {
+  SSTS0200 rcvr;
+
+  if (get_ibmi_system_status(&rcvr))
+    return 0;
+
+  return (uint64_t)rcvr.main_storage_size * 1024ULL;
+}
+
+
+uint64_t uv_get_total_memory(void) {
+  SSTS0200 rcvr;
+
+  if (get_ibmi_system_status(&rcvr))
+    return 0;
+
+  return (uint64_t)rcvr.main_storage_size * 1024ULL;
+}
+
+
+uint64_t uv_get_constrained_memory(void) {
+  return 0;  /* Memory constraints are unknown. */
+}
+
+
+void uv_loadavg(double avg[3]) {
+  SSTS0200 rcvr;
+
+  if (get_ibmi_system_status(&rcvr)) {
+    avg[0] = avg[1] = avg[2] = 0;
+    return;
+  }
+
+  /* The average (in tenths) of the elapsed time during which the processing
+   * units were in use. For example, a value of 411 in binary would be 41.1%.
+   * This percentage could be greater than 100% for an uncapped partition.
+   */
+  double processing_unit_used_percent =
+    rcvr.percent_processing_unit_used / 1000.0;
+
+  avg[0] = avg[1] = avg[2] = processing_unit_used_percent;
+}
+
+
+int uv_resident_set_memory(size_t* rss) {
+  *rss = 0;
+  return 0;
+}
+
+
+int uv_uptime(double* uptime) {
+  return UV_ENOSYS;
+}
+
+
+int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) {
+  unsigned int numcpus, idx = 0;
+  uv_cpu_info_t* cpu_info;
+
+  *cpu_infos = NULL;
+  *count = 0;
+
+  numcpus = sysconf(_SC_NPROCESSORS_ONLN);
+
+  *cpu_infos = (uv_cpu_info_t*)uv__malloc(numcpus * sizeof(uv_cpu_info_t));
+  if (!*cpu_infos) {
+    return UV_ENOMEM;
+  }
+
+  cpu_info = *cpu_infos;
+  for (idx = 0; idx < numcpus; idx++) {
+    cpu_info->speed = 0;
+    cpu_info->model = uv__strdup("unknown");
+    cpu_info->cpu_times.user = 0;
+    cpu_info->cpu_times.sys = 0;
+    cpu_info->cpu_times.idle = 0;
+    cpu_info->cpu_times.irq = 0;
+    cpu_info->cpu_times.nice = 0;
+    cpu_info++;
+  }
+  *count = numcpus;
+
+  return 0;
+}
+
+
+static int get_ibmi_physical_address(const char* line, char (*phys_addr)[6]) {
+  LIND0500 rcvr;
+  /* rcvrlen is input parameter 2 to QDCRLIND */
+  unsigned int rcvrlen = sizeof(rcvr);
+  unsigned char format[8], line_name[10];
+  unsigned char mac_addr[sizeof(rcvr.loca_adapter_address)];
+  int c[6];
+
+  /* format is input parameter 3 to QDCRLIND */
+  iconv_a2e("LIND0500", format, sizeof(format));
+
+  /* line_name is input parameter 4 to QDCRLIND */
+  iconv_a2e(line, line_name, sizeof(line_name));
+
+  /* err is input parameter 5 to QDCRLIND */
+  errcode_s err;
+
+  /* qwcrssts_pointer is the 16-byte tagged system pointer to QDCRLIND */
+  ILEpointer __attribute__((aligned(16))) qdcrlind_pointer;
+
+  /* qwcrssts_argv is the array of argument pointers to QDCRLIND */
+  void* qdcrlind_argv[6];
+
+  /* Set the IBM i pointer to the QSYS/QDCRLIND *PGM object */
+  int rc = _RSLOBJ2(&qdcrlind_pointer, RSLOBJ_TS_PGM, "QDCRLIND", "QSYS");
+
+  if (rc != 0)
+    return rc;
+
+  /* initialize the QDCRLIND returned info structure */
+  memset(&rcvr, 0, sizeof(rcvr));
+
+  /* initialize the QDCRLIND error code structure */
+  memset(&err, 0, sizeof(err));
+  err.bytes_provided = sizeof(err);
+
+  /* initialize the array of argument pointers for the QDCRLIND API */
+  qdcrlind_argv[0] = &rcvr;
+  qdcrlind_argv[1] = &rcvrlen;
+  qdcrlind_argv[2] = &format;
+  qdcrlind_argv[3] = &line_name;
+  qdcrlind_argv[4] = &err;
+  qdcrlind_argv[5] = NULL;
+
+  /* Call the IBM i QDCRLIND API from PASE */
+  rc = _PGMCALL(&qdcrlind_pointer, qdcrlind_argv, 0);
+  if (rc != 0)
+    return rc;
+
+  if (err.bytes_available > 0) {
+    return -1;
+  }
+
+  /* convert ebcdic loca_adapter_address to ascii first */
+  iconv_e2a(rcvr.loca_adapter_address, mac_addr,
+            sizeof(rcvr.loca_adapter_address));
+
+  /* convert loca_adapter_address(char[12]) to phys_addr(char[6]) */
+  int r = sscanf(mac_addr, "%02x%02x%02x%02x%02x%02x",
+                &c[0], &c[1], &c[2], &c[3], &c[4], &c[5]);
+
+  if (r == ARRAY_SIZE(c)) {
+    (*phys_addr)[0] = c[0];
+    (*phys_addr)[1] = c[1];
+    (*phys_addr)[2] = c[2];
+    (*phys_addr)[3] = c[3];
+    (*phys_addr)[4] = c[4];
+    (*phys_addr)[5] = c[5];
+  } else {
+    memset(*phys_addr, 0, sizeof(*phys_addr));
+    rc = -1;
+  }
+  return rc;
+}
+
+
+int uv_interface_addresses(uv_interface_address_t** addresses, int* count) {
+  uv_interface_address_t* address;
+  struct ifaddrs_pase *ifap = NULL, *cur;
+  int inet6, r = 0;
+
+  *count = 0;
+  *addresses = NULL;
+
+  if (Qp2getifaddrs(&ifap))
+    return UV_ENOSYS;
+
+  /* The first loop to get the size of the array to be allocated */
+  for (cur = ifap; cur; cur = cur->ifa_next) {
+    if (!(cur->ifa_addr->sa_family == AF_INET6 ||
+          cur->ifa_addr->sa_family == AF_INET))
+      continue;
+
+    if (!(cur->ifa_flags & IFF_UP && cur->ifa_flags & IFF_RUNNING))
+      continue;
+
+    (*count)++;
+  }
+
+  if (*count == 0) {
+    Qp2freeifaddrs(ifap);
+    return 0;
+  }
+
+  /* Alloc the return interface structs */
+  *addresses = uv__calloc(*count, sizeof(**addresses));
+  if (*addresses == NULL) {
+    Qp2freeifaddrs(ifap);
+    return UV_ENOMEM;
+  }
+  address = *addresses;
+
+  /* The second loop to fill in the array */
+  for (cur = ifap; cur; cur = cur->ifa_next) {
+    if (!(cur->ifa_addr->sa_family == AF_INET6 ||
+          cur->ifa_addr->sa_family == AF_INET))
+      continue;
+
+    if (!(cur->ifa_flags & IFF_UP && cur->ifa_flags & IFF_RUNNING))
+      continue;
+
+    address->name = uv__strdup(cur->ifa_name);
+
+    inet6 = (cur->ifa_addr->sa_family == AF_INET6);
+
+    if (inet6) {
+      address->address.address6 = *((struct sockaddr_in6*)cur->ifa_addr);
+      address->netmask.netmask6 = *((struct sockaddr_in6*)cur->ifa_netmask);
+      address->netmask.netmask6.sin6_family = AF_INET6;
+    } else {
+      address->address.address4 = *((struct sockaddr_in*)cur->ifa_addr);
+      address->netmask.netmask4 = *((struct sockaddr_in*)cur->ifa_netmask);
+      address->netmask.netmask4.sin_family = AF_INET;
+    }
+    address->is_internal = cur->ifa_flags & IFF_LOOPBACK ? 1 : 0;
+    if (!address->is_internal) {
+      int rc = -1;
+      size_t name_len = strlen(address->name);
+      /* To get the associated MAC address, we must convert the address to a
+       * line description. Normally, the name field contains the line
+       * description name, but for VLANs it has the VLAN appended with a
+       * period. Since object names can also contain periods and numbers, there
+       * is no way to know if a returned name is for a VLAN or not. eg.
+       * *LIND ETH1.1 and *LIND ETH1, VLAN 1 both have the same name: ETH1.1
+       *
+       * Instead, we apply the same heuristic used by some of the XPF ioctls:
+       * - names > 10 *must* contain a VLAN
+       * - assume names <= 10 do not contain a VLAN and try directly
+       * - if >10 or QDCRLIND returned an error, try to strip off a VLAN
+       *   and try again
+       * - if we still get an error or couldn't find a period, leave the MAC as
+       *   00:00:00:00:00:00
+       */
+      if (name_len <= 10) {
+        /* Assume name does not contain a VLAN ID */
+        rc = get_ibmi_physical_address(address->name, &address->phys_addr);
+      }
+
+      if (name_len > 10 || rc != 0) {
+        /* The interface name must contain a VLAN ID suffix. Attempt to strip
+         * it off so we can get the line description to pass to QDCRLIND.
+         */
+        char* temp_name = uv__strdup(address->name);
+        char* dot = strrchr(temp_name, '.');
+        if (dot != NULL) {
+          *dot = '\0';
+          if (strlen(temp_name) <= 10) {
+            rc = get_ibmi_physical_address(temp_name, &address->phys_addr);
+          }
+        }
+        uv__free(temp_name);
+      }
+    }
+
+    address++;
+  }
+
+  Qp2freeifaddrs(ifap);
+  return r;
+}
+
+
+void uv_free_interface_addresses(uv_interface_address_t* addresses, int count) {
+  int i;
+
+  for (i = 0; i < count; ++i) {
+    uv__free(addresses[i].name);
+  }
+
+  uv__free(addresses);
+}
+
+char** uv_setup_args(int argc, char** argv) {
+  char exepath[UV__PATH_MAX];
+  char* s;
+  size_t size;
+
+  if (argc > 0) {
+    /* Use argv[0] to determine value for uv_exepath(). */
+    size = sizeof(exepath);
+    if (uv__search_path(argv[0], exepath, &size) == 0) {
+      uv_once(&process_title_mutex_once, init_process_title_mutex_once);
+      uv_mutex_lock(&process_title_mutex);
+      original_exepath = uv__strdup(exepath);
+      uv_mutex_unlock(&process_title_mutex);
+    }
+  }
+
+  return argv;
+}
+
+int uv_set_process_title(const char* title) {
+  return 0;
+}
+
+int uv_get_process_title(char* buffer, size_t size) {
+  if (buffer == NULL || size == 0)
+    return UV_EINVAL;
+
+  buffer[0] = '\0';
+  return 0;
+}
+
+void uv__process_title_cleanup(void) {
+}
diff --git a/wpinet/src/main/native/thirdparty/libuv/src/unix/internal.h b/wpinet/src/main/native/thirdparty/libuv/src/unix/internal.h
new file mode 100644
index 0000000..2b65415
--- /dev/null
+++ b/wpinet/src/main/native/thirdparty/libuv/src/unix/internal.h
@@ -0,0 +1,371 @@
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#ifndef UV_UNIX_INTERNAL_H_
+#define UV_UNIX_INTERNAL_H_
+
+#include "uv-common.h"
+
+#include <assert.h>
+#include <limits.h> /* _POSIX_PATH_MAX, PATH_MAX */
+#include <stdlib.h> /* abort */
+#include <string.h> /* strrchr */
+#include <fcntl.h>  /* O_CLOEXEC and O_NONBLOCK, if supported. */
+#include <stdio.h>
+#include <errno.h>
+#include <sys/socket.h>
+
+#if defined(__STRICT_ANSI__)
+# define inline __inline
+#endif
+
+#if defined(__linux__)
+# include "linux-syscalls.h"
+#endif /* __linux__ */
+
+#if defined(__MVS__)
+# include "os390-syscalls.h"
+#endif /* __MVS__ */
+
+#if defined(__sun)
+# include <sys/port.h>
+# include <port.h>
+#endif /* __sun */
+
+#if defined(_AIX)
+# define reqevents events
+# define rtnevents revents
+# include <sys/poll.h>
+#else
+# include <poll.h>
+#endif /* _AIX */
+
+#if defined(__APPLE__) && !TARGET_OS_IPHONE
+# include <AvailabilityMacros.h>
+#endif
+
+/*
+ * Define common detection for active Thread Sanitizer
+ * - clang uses __has_feature(thread_sanitizer)
+ * - gcc-7+ uses __SANITIZE_THREAD__
+ */
+#if defined(__has_feature)
+# if __has_feature(thread_sanitizer)
+#  define __SANITIZE_THREAD__ 1
+# endif
+#endif
+
+#if defined(PATH_MAX)
+# define UV__PATH_MAX PATH_MAX
+#else
+# define UV__PATH_MAX 8192
+#endif
+
+#if defined(__ANDROID__)
+int uv__pthread_sigmask(int how, const sigset_t* set, sigset_t* oset);
+# ifdef pthread_sigmask
+# undef pthread_sigmask
+# endif
+# define pthread_sigmask(how, set, oldset) uv__pthread_sigmask(how, set, oldset)
+#endif
+
+#define ACCESS_ONCE(type, var)                                                \
+  (*(volatile type*) &(var))
+
+#define ROUND_UP(a, b)                                                        \
+  ((a) % (b) ? ((a) + (b)) - ((a) % (b)) : (a))
+
+#define UNREACHABLE()                                                         \
+  do {                                                                        \
+    assert(0 && "unreachable code");                                          \
+    abort();                                                                  \
+  }                                                                           \
+  while (0)
+
+#define SAVE_ERRNO(block)                                                     \
+  do {                                                                        \
+    int _saved_errno = errno;                                                 \
+    do { block; } while (0);                                                  \
+    errno = _saved_errno;                                                     \
+  }                                                                           \
+  while (0)
+
+/* The __clang__ and __INTEL_COMPILER checks are superfluous because they
+ * define __GNUC__. They are here to convey to you, dear reader, that these
+ * macros are enabled when compiling with clang or icc.
+ */
+#if defined(__clang__) ||                                                     \
+    defined(__GNUC__) ||                                                      \
+    defined(__INTEL_COMPILER)
+# define UV_UNUSED(declaration)     __attribute__((unused)) declaration
+#else
+# define UV_UNUSED(declaration)     declaration
+#endif
+
+/* Leans on the fact that, on Linux, POLLRDHUP == EPOLLRDHUP. */
+#ifdef POLLRDHUP
+# define UV__POLLRDHUP POLLRDHUP
+#else
+# define UV__POLLRDHUP 0x2000
+#endif
+
+#ifdef POLLPRI
+# define UV__POLLPRI POLLPRI
+#else
+# define UV__POLLPRI 0
+#endif
+
+#if !defined(O_CLOEXEC) && defined(__FreeBSD__)
+/*
+ * It may be that we are just missing `__POSIX_VISIBLE >= 200809`.
+ * Try using fixed value const and give up, if it doesn't work
+ */
+# define O_CLOEXEC 0x00100000
+#endif
+
+typedef struct uv__stream_queued_fds_s uv__stream_queued_fds_t;
+
+/* loop flags */
+enum {
+  UV_LOOP_BLOCK_SIGPROF = 0x1,
+  UV_LOOP_REAP_CHILDREN = 0x2
+};
+
+/* flags of excluding ifaddr */
+enum {
+  UV__EXCLUDE_IFPHYS,
+  UV__EXCLUDE_IFADDR
+};
+
+typedef enum {
+  UV_CLOCK_PRECISE = 0,  /* Use the highest resolution clock available. */
+  UV_CLOCK_FAST = 1      /* Use the fastest clock with <= 1ms granularity. */
+} uv_clocktype_t;
+
+struct uv__stream_queued_fds_s {
+  unsigned int size;
+  unsigned int offset;
+  int fds[1];
+};
+
+
+#if defined(_AIX) || \
+    defined(__APPLE__) || \
+    defined(__DragonFly__) || \
+    defined(__FreeBSD__) || \
+    defined(__FreeBSD_kernel__) || \
+    defined(__linux__) || \
+    defined(__OpenBSD__) || \
+    defined(__NetBSD__)
+#define uv__nonblock uv__nonblock_ioctl
+#define UV__NONBLOCK_IS_IOCTL 1
+#else
+#define uv__nonblock uv__nonblock_fcntl
+#define UV__NONBLOCK_IS_IOCTL 0
+#endif
+
+/* On Linux, uv__nonblock_fcntl() and uv__nonblock_ioctl() do not commute
+ * when O_NDELAY is not equal to O_NONBLOCK.  Case in point: linux/sparc32
+ * and linux/sparc64, where O_NDELAY is O_NONBLOCK + another bit.
+ *
+ * Libuv uses uv__nonblock_fcntl() directly sometimes so ensure that it
+ * commutes with uv__nonblock().
+ */
+#if defined(__linux__) && O_NDELAY != O_NONBLOCK
+#undef uv__nonblock
+#define uv__nonblock uv__nonblock_fcntl
+#undef UV__NONBLOCK_IS_IOCTL
+#define UV__NONBLOCK_IS_FCNTL
+#endif
+
+/* core */
+int uv__cloexec(int fd, int set);
+int uv__nonblock_ioctl(int fd, int set);
+int uv__nonblock_fcntl(int fd, int set);
+int uv__close(int fd); /* preserves errno */
+int uv__close_nocheckstdio(int fd);
+int uv__close_nocancel(int fd);
+int uv__socket(int domain, int type, int protocol);
+ssize_t uv__recvmsg(int fd, struct msghdr *msg, int flags);
+void uv__make_close_pending(uv_handle_t* handle);
+int uv__getiovmax(void);
+
+void uv__io_init(uv__io_t* w, uv__io_cb cb, int fd);
+void uv__io_start(uv_loop_t* loop, uv__io_t* w, unsigned int events);
+void uv__io_stop(uv_loop_t* loop, uv__io_t* w, unsigned int events);
+void uv__io_close(uv_loop_t* loop, uv__io_t* w);
+void uv__io_feed(uv_loop_t* loop, uv__io_t* w);
+int uv__io_active(const uv__io_t* w, unsigned int events);
+int uv__io_check_fd(uv_loop_t* loop, int fd);
+void uv__io_poll(uv_loop_t* loop, int timeout); /* in milliseconds or -1 */
+int uv__io_fork(uv_loop_t* loop);
+int uv__fd_exists(uv_loop_t* loop, int fd);
+
+/* async */
+void uv__async_stop(uv_loop_t* loop);
+int uv__async_fork(uv_loop_t* loop);
+
+
+/* loop */
+void uv__run_idle(uv_loop_t* loop);
+void uv__run_check(uv_loop_t* loop);
+void uv__run_prepare(uv_loop_t* loop);
+
+/* stream */
+void uv__stream_init(uv_loop_t* loop, uv_stream_t* stream,
+    uv_handle_type type);
+int uv__stream_open(uv_stream_t*, int fd, int flags);
+void uv__stream_destroy(uv_stream_t* stream);
+#if defined(__APPLE__)
+int uv__stream_try_select(uv_stream_t* stream, int* fd);
+#endif /* defined(__APPLE__) */
+void uv__server_io(uv_loop_t* loop, uv__io_t* w, unsigned int events);
+int uv__accept(int sockfd);
+int uv__dup2_cloexec(int oldfd, int newfd);
+int uv__open_cloexec(const char* path, int flags);
+int uv__slurp(const char* filename, char* buf, size_t len);
+
+/* tcp */
+int uv__tcp_listen(uv_tcp_t* tcp, int backlog, uv_connection_cb cb);
+int uv__tcp_nodelay(int fd, int on);
+int uv__tcp_keepalive(int fd, int on, unsigned int delay);
+
+/* pipe */
+int uv__pipe_listen(uv_pipe_t* handle, int backlog, uv_connection_cb cb);
+
+/* signal */
+void uv__signal_close(uv_signal_t* handle);
+void uv__signal_global_once_init(void);
+void uv__signal_loop_cleanup(uv_loop_t* loop);
+int uv__signal_loop_fork(uv_loop_t* loop);
+
+/* platform specific */
+uint64_t uv__hrtime(uv_clocktype_t type);
+int uv__kqueue_init(uv_loop_t* loop);
+int uv__epoll_init(uv_loop_t* loop);
+int uv__platform_loop_init(uv_loop_t* loop);
+void uv__platform_loop_delete(uv_loop_t* loop);
+void uv__platform_invalidate_fd(uv_loop_t* loop, int fd);
+
+/* various */
+void uv__async_close(uv_async_t* handle);
+void uv__check_close(uv_check_t* handle);
+void uv__fs_event_close(uv_fs_event_t* handle);
+void uv__idle_close(uv_idle_t* handle);
+void uv__pipe_close(uv_pipe_t* handle);
+void uv__poll_close(uv_poll_t* handle);
+void uv__prepare_close(uv_prepare_t* handle);
+void uv__process_close(uv_process_t* handle);
+void uv__stream_close(uv_stream_t* handle);
+void uv__tcp_close(uv_tcp_t* handle);
+size_t uv__thread_stack_size(void);
+void uv__udp_close(uv_udp_t* handle);
+void uv__udp_finish_close(uv_udp_t* handle);
+FILE* uv__open_file(const char* path);
+int uv__getpwuid_r(uv_passwd_t* pwd);
+int uv__search_path(const char* prog, char* buf, size_t* buflen);
+void uv__wait_children(uv_loop_t* loop);
+
+/* random */
+int uv__random_devurandom(void* buf, size_t buflen);
+int uv__random_getrandom(void* buf, size_t buflen);
+int uv__random_getentropy(void* buf, size_t buflen);
+int uv__random_readpath(const char* path, void* buf, size_t buflen);
+int uv__random_sysctl(void* buf, size_t buflen);
+
+#if defined(__APPLE__)
+int uv___stream_fd(const uv_stream_t* handle);
+#define uv__stream_fd(handle) (uv___stream_fd((const uv_stream_t*) (handle)))
+#else
+#define uv__stream_fd(handle) ((handle)->io_watcher.fd)
+#endif /* defined(__APPLE__) */
+
+int uv__make_pipe(int fds[2], int flags);
+
+#if defined(__APPLE__)
+
+int uv__fsevents_init(uv_fs_event_t* handle);
+int uv__fsevents_close(uv_fs_event_t* handle);
+void uv__fsevents_loop_delete(uv_loop_t* loop);
+
+#endif /* defined(__APPLE__) */
+
+UV_UNUSED(static void uv__update_time(uv_loop_t* loop)) {
+  /* Use a fast time source if available.  We only need millisecond precision.
+   */
+  loop->time = uv__hrtime(UV_CLOCK_FAST) / 1000000;
+}
+
+UV_UNUSED(static const char* uv__basename_r(const char* path)) {
+  const char* s;
+
+  s = strrchr(path, '/');
+  if (s == NULL)
+    return (char*) path;
+
+  return s + 1;
+}
+
+#if defined(__linux__)
+int uv__inotify_fork(uv_loop_t* loop, void* old_watchers);
+#endif
+
+typedef int (*uv__peersockfunc)(int, struct sockaddr*, socklen_t*);
+
+int uv__getsockpeername(const uv_handle_t* handle,
+                        uv__peersockfunc func,
+                        struct sockaddr* name,
+                        int* namelen);
+
+#if defined(__linux__)            ||                                      \
+    defined(__FreeBSD__)          ||                                      \
+    defined(__FreeBSD_kernel__)   ||                                       \
+    defined(__DragonFly__)
+#define HAVE_MMSG 1
+struct uv__mmsghdr {
+  struct msghdr msg_hdr;
+  unsigned int msg_len;
+};
+
+int uv__recvmmsg(int fd, struct uv__mmsghdr* mmsg, unsigned int vlen);
+int uv__sendmmsg(int fd, struct uv__mmsghdr* mmsg, unsigned int vlen);
+#else
+#define HAVE_MMSG 0
+#endif
+
+#if defined(__sun)
+#if !defined(_POSIX_VERSION) || _POSIX_VERSION < 200809L
+size_t strnlen(const char* s, size_t maxlen);
+#endif
+#endif
+
+#if defined(__FreeBSD__)
+ssize_t
+uv__fs_copy_file_range(int fd_in,
+                       off_t* off_in,
+                       int fd_out,
+                       off_t* off_out,
+                       size_t len,
+                       unsigned int flags);
+#endif
+
+
+#endif /* UV_UNIX_INTERNAL_H_ */
diff --git a/wpinet/src/main/native/thirdparty/libuv/src/unix/kqueue.cpp b/wpinet/src/main/native/thirdparty/libuv/src/unix/kqueue.cpp
new file mode 100644
index 0000000..86eb529
--- /dev/null
+++ b/wpinet/src/main/native/thirdparty/libuv/src/unix/kqueue.cpp
@@ -0,0 +1,605 @@
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include "uv.h"
+#include "internal.h"
+
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include <sys/sysctl.h>
+#include <sys/types.h>
+#include <sys/event.h>
+#include <sys/time.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <time.h>
+
+/*
+ * Required on
+ * - Until at least FreeBSD 11.0
+ * - Older versions of Mac OS X
+ *
+ * http://www.boost.org/doc/libs/1_61_0/boost/asio/detail/kqueue_reactor.hpp
+ */
+#ifndef EV_OOBAND
+#define EV_OOBAND  EV_FLAG1
+#endif
+
+static void uv__fs_event(uv_loop_t* loop, uv__io_t* w, unsigned int fflags);
+
+
+int uv__kqueue_init(uv_loop_t* loop) {
+  loop->backend_fd = kqueue();
+  if (loop->backend_fd == -1)
+    return UV__ERR(errno);
+
+  uv__cloexec(loop->backend_fd, 1);
+
+  return 0;
+}
+
+
+#if defined(__APPLE__) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
+static int uv__has_forked_with_cfrunloop;
+#endif
+
+int uv__io_fork(uv_loop_t* loop) {
+  int err;
+  loop->backend_fd = -1;
+  err = uv__kqueue_init(loop);
+  if (err)
+    return err;
+
+#if defined(__APPLE__) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
+  if (loop->cf_state != NULL) {
+    /* We cannot start another CFRunloop and/or thread in the child
+       process; CF aborts if you try or if you try to touch the thread
+       at all to kill it. So the best we can do is ignore it from now
+       on. This means we can't watch directories in the same way
+       anymore (like other BSDs). It also means we cannot properly
+       clean up the allocated resources; calling
+       uv__fsevents_loop_delete from uv_loop_close will crash the
+       process. So we sidestep the issue by pretending like we never
+       started it in the first place.
+    */
+    uv__store_relaxed(&uv__has_forked_with_cfrunloop, 1);
+    uv__free(loop->cf_state);
+    loop->cf_state = NULL;
+  }
+#endif /* #if defined(__APPLE__) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 */
+  return err;
+}
+
+
+int uv__io_check_fd(uv_loop_t* loop, int fd) {
+  struct kevent ev;
+  int rc;
+
+  rc = 0;
+  EV_SET(&ev, fd, EVFILT_READ, EV_ADD, 0, 0, 0);
+  if (kevent(loop->backend_fd, &ev, 1, NULL, 0, NULL))
+    rc = UV__ERR(errno);
+
+  EV_SET(&ev, fd, EVFILT_READ, EV_DELETE, 0, 0, 0);
+  if (rc == 0)
+    if (kevent(loop->backend_fd, &ev, 1, NULL, 0, NULL))
+      abort();
+
+  return rc;
+}
+
+
+void uv__io_poll(uv_loop_t* loop, int timeout) {
+  struct kevent events[1024];
+  struct kevent* ev;
+  struct timespec spec;
+  unsigned int nevents;
+  unsigned int revents;
+  QUEUE* q;
+  uv__io_t* w;
+  uv_process_t* process;
+  sigset_t* pset;
+  sigset_t set;
+  uint64_t base;
+  uint64_t diff;
+  int have_signals;
+  int filter;
+  int fflags;
+  int count;
+  int nfds;
+  int fd;
+  int op;
+  int i;
+  int user_timeout;
+  int reset_timeout;
+
+  if (loop->nfds == 0) {
+    assert(QUEUE_EMPTY(&loop->watcher_queue));
+    return;
+  }
+
+  nevents = 0;
+
+  while (!QUEUE_EMPTY(&loop->watcher_queue)) {
+    q = QUEUE_HEAD(&loop->watcher_queue);
+    QUEUE_REMOVE(q);
+    QUEUE_INIT(q);
+
+    w = QUEUE_DATA(q, uv__io_t, watcher_queue);
+    assert(w->pevents != 0);
+    assert(w->fd >= 0);
+    assert(w->fd < (int) loop->nwatchers);
+
+    if ((w->events & POLLIN) == 0 && (w->pevents & POLLIN) != 0) {
+      filter = EVFILT_READ;
+      fflags = 0;
+      op = EV_ADD;
+
+      if (w->cb == uv__fs_event) {
+        filter = EVFILT_VNODE;
+        fflags = NOTE_ATTRIB | NOTE_WRITE  | NOTE_RENAME
+               | NOTE_DELETE | NOTE_EXTEND | NOTE_REVOKE;
+        op = EV_ADD | EV_ONESHOT; /* Stop the event from firing repeatedly. */
+      }
+
+      EV_SET(events + nevents, w->fd, filter, op, fflags, 0, 0);
+
+      if (++nevents == ARRAY_SIZE(events)) {
+        if (kevent(loop->backend_fd, events, nevents, NULL, 0, NULL))
+          abort();
+        nevents = 0;
+      }
+    }
+
+    if ((w->events & POLLOUT) == 0 && (w->pevents & POLLOUT) != 0) {
+      EV_SET(events + nevents, w->fd, EVFILT_WRITE, EV_ADD, 0, 0, 0);
+
+      if (++nevents == ARRAY_SIZE(events)) {
+        if (kevent(loop->backend_fd, events, nevents, NULL, 0, NULL))
+          abort();
+        nevents = 0;
+      }
+    }
+
+   if ((w->events & UV__POLLPRI) == 0 && (w->pevents & UV__POLLPRI) != 0) {
+      EV_SET(events + nevents, w->fd, EV_OOBAND, EV_ADD, 0, 0, 0);
+
+      if (++nevents == ARRAY_SIZE(events)) {
+        if (kevent(loop->backend_fd, events, nevents, NULL, 0, NULL))
+          abort();
+        nevents = 0;
+      }
+    }
+
+    w->events = w->pevents;
+  }
+
+  pset = NULL;
+  if (loop->flags & UV_LOOP_BLOCK_SIGPROF) {
+    pset = &set;
+    sigemptyset(pset);
+    sigaddset(pset, SIGPROF);
+  }
+
+  assert(timeout >= -1);
+  base = loop->time;
+  count = 48; /* Benchmarks suggest this gives the best throughput. */
+
+  if (uv__get_internal_fields(loop)->flags & UV_METRICS_IDLE_TIME) {
+    reset_timeout = 1;
+    user_timeout = timeout;
+    timeout = 0;
+  } else {
+    reset_timeout = 0;
+  }
+
+  for (;; nevents = 0) {
+    /* Only need to set the provider_entry_time if timeout != 0. The function
+     * will return early if the loop isn't configured with UV_METRICS_IDLE_TIME.
+     */
+    if (timeout != 0)
+      uv__metrics_set_provider_entry_time(loop);
+
+    if (timeout != -1) {
+      spec.tv_sec = timeout / 1000;
+      spec.tv_nsec = (timeout % 1000) * 1000000;
+    }
+
+    if (pset != NULL)
+      pthread_sigmask(SIG_BLOCK, pset, NULL);
+
+    nfds = kevent(loop->backend_fd,
+                  events,
+                  nevents,
+                  events,
+                  ARRAY_SIZE(events),
+                  timeout == -1 ? NULL : &spec);
+
+    if (pset != NULL)
+      pthread_sigmask(SIG_UNBLOCK, pset, NULL);
+
+    /* Update loop->time unconditionally. It's tempting to skip the update when
+     * timeout == 0 (i.e. non-blocking poll) but there is no guarantee that the
+     * operating system didn't reschedule our process while in the syscall.
+     */
+    SAVE_ERRNO(uv__update_time(loop));
+
+    if (nfds == 0) {
+      if (reset_timeout != 0) {
+        timeout = user_timeout;
+        reset_timeout = 0;
+        if (timeout == -1)
+          continue;
+        if (timeout > 0)
+          goto update_timeout;
+      }
+
+      assert(timeout != -1);
+      return;
+    }
+
+    if (nfds == -1) {
+      if (errno != EINTR)
+        abort();
+
+      if (reset_timeout != 0) {
+        timeout = user_timeout;
+        reset_timeout = 0;
+      }
+
+      if (timeout == 0)
+        return;
+
+      if (timeout == -1)
+        continue;
+
+      /* Interrupted by a signal. Update timeout and poll again. */
+      goto update_timeout;
+    }
+
+    have_signals = 0;
+    nevents = 0;
+
+    assert(loop->watchers != NULL);
+    loop->watchers[loop->nwatchers] = (uv__io_t*) events;
+    loop->watchers[loop->nwatchers + 1] = (uv__io_t*) (uintptr_t) nfds;
+    for (i = 0; i < nfds; i++) {
+      ev = events + i;
+      fd = ev->ident;
+
+      /* Handle kevent NOTE_EXIT results */
+      if (ev->filter == EVFILT_PROC) {
+        QUEUE_FOREACH(q, &loop->process_handles) {
+          process = QUEUE_DATA(q, uv_process_t, queue);
+          if (process->pid == fd) {
+            process->flags |= UV_HANDLE_REAP;
+            loop->flags |= UV_LOOP_REAP_CHILDREN;
+            break;
+          }
+        }
+        nevents++;
+        continue;
+      }
+
+      /* Skip invalidated events, see uv__platform_invalidate_fd */
+      if (fd == -1)
+        continue;
+      w = (uv__io_t*)loop->watchers[fd];
+
+      if (w == NULL) {
+        /* File descriptor that we've stopped watching, disarm it.
+         * TODO: batch up. */
+        struct kevent events[1];
+
+        EV_SET(events + 0, fd, ev->filter, EV_DELETE, 0, 0, 0);
+        if (kevent(loop->backend_fd, events, 1, NULL, 0, NULL))
+          if (errno != EBADF && errno != ENOENT)
+            abort();
+
+        continue;
+      }
+
+      if (ev->filter == EVFILT_VNODE) {
+        assert(w->events == POLLIN);
+        assert(w->pevents == POLLIN);
+        uv__metrics_update_idle_time(loop);
+        w->cb(loop, w, ev->fflags); /* XXX always uv__fs_event() */
+        nevents++;
+        continue;
+      }
+
+      revents = 0;
+
+      if (ev->filter == EVFILT_READ) {
+        if (w->pevents & POLLIN) {
+          revents |= POLLIN;
+          w->rcount = ev->data;
+        } else {
+          /* TODO batch up */
+          struct kevent events[1];
+          EV_SET(events + 0, fd, ev->filter, EV_DELETE, 0, 0, 0);
+          if (kevent(loop->backend_fd, events, 1, NULL, 0, NULL))
+            if (errno != ENOENT)
+              abort();
+        }
+        if ((ev->flags & EV_EOF) && (w->pevents & UV__POLLRDHUP))
+          revents |= UV__POLLRDHUP;
+      }
+
+      if (ev->filter == EV_OOBAND) {
+        if (w->pevents & UV__POLLPRI) {
+          revents |= UV__POLLPRI;
+          w->rcount = ev->data;
+        } else {
+          /* TODO batch up */
+          struct kevent events[1];
+          EV_SET(events + 0, fd, ev->filter, EV_DELETE, 0, 0, 0);
+          if (kevent(loop->backend_fd, events, 1, NULL, 0, NULL))
+            if (errno != ENOENT)
+              abort();
+        }
+      }
+
+      if (ev->filter == EVFILT_WRITE) {
+        if (w->pevents & POLLOUT) {
+          revents |= POLLOUT;
+          w->wcount = ev->data;
+        } else {
+          /* TODO batch up */
+          struct kevent events[1];
+          EV_SET(events + 0, fd, ev->filter, EV_DELETE, 0, 0, 0);
+          if (kevent(loop->backend_fd, events, 1, NULL, 0, NULL))
+            if (errno != ENOENT)
+              abort();
+        }
+      }
+
+      if (ev->flags & EV_ERROR)
+        revents |= POLLERR;
+
+      if (revents == 0)
+        continue;
+
+      /* Run signal watchers last.  This also affects child process watchers
+       * because those are implemented in terms of signal watchers.
+       */
+      if (w == &loop->signal_io_watcher) {
+        have_signals = 1;
+      } else {
+        uv__metrics_update_idle_time(loop);
+        w->cb(loop, w, revents);
+      }
+
+      nevents++;
+    }
+
+    if (loop->flags & UV_LOOP_REAP_CHILDREN) {
+      loop->flags &= ~UV_LOOP_REAP_CHILDREN;
+      uv__wait_children(loop);
+    }
+
+    if (reset_timeout != 0) {
+      timeout = user_timeout;
+      reset_timeout = 0;
+    }
+
+    if (have_signals != 0) {
+      uv__metrics_update_idle_time(loop);
+      loop->signal_io_watcher.cb(loop, &loop->signal_io_watcher, POLLIN);
+    }
+
+    loop->watchers[loop->nwatchers] = NULL;
+    loop->watchers[loop->nwatchers + 1] = NULL;
+
+    if (have_signals != 0)
+      return;  /* Event loop should cycle now so don't poll again. */
+
+    if (nevents != 0) {
+      if (nfds == ARRAY_SIZE(events) && --count != 0) {
+        /* Poll for more events but don't block this time. */
+        timeout = 0;
+        continue;
+      }
+      return;
+    }
+
+    if (timeout == 0)
+      return;
+
+    if (timeout == -1)
+      continue;
+
+update_timeout:
+    assert(timeout > 0);
+
+    diff = loop->time - base;
+    if (diff >= (uint64_t) timeout)
+      return;
+
+    timeout -= diff;
+  }
+}
+
+
+void uv__platform_invalidate_fd(uv_loop_t* loop, int fd) {
+  struct kevent* events;
+  uintptr_t i;
+  uintptr_t nfds;
+
+  assert(loop->watchers != NULL);
+  assert(fd >= 0);
+
+  events = (struct kevent*) loop->watchers[loop->nwatchers];
+  nfds = (uintptr_t) loop->watchers[loop->nwatchers + 1];
+  if (events == NULL)
+    return;
+
+  /* Invalidate events with same file descriptor */
+  for (i = 0; i < nfds; i++)
+    if ((int) events[i].ident == fd && events[i].filter != EVFILT_PROC)
+      events[i].ident = -1;
+}
+
+
+static void uv__fs_event(uv_loop_t* loop, uv__io_t* w, unsigned int fflags) {
+  uv_fs_event_t* handle;
+  struct kevent ev;
+  int events;
+  const char* path;
+#if defined(F_GETPATH)
+  /* MAXPATHLEN == PATH_MAX but the former is what XNU calls it internally. */
+  char pathbuf[MAXPATHLEN];
+#endif
+
+  handle = container_of(w, uv_fs_event_t, event_watcher);
+
+  if (fflags & (NOTE_ATTRIB | NOTE_EXTEND))
+    events = UV_CHANGE;
+  else
+    events = UV_RENAME;
+
+  path = NULL;
+#if defined(F_GETPATH)
+  /* Also works when the file has been unlinked from the file system. Passing
+   * in the path when the file has been deleted is arguably a little strange
+   * but it's consistent with what the inotify backend does.
+   */
+  if (fcntl(handle->event_watcher.fd, F_GETPATH, pathbuf) == 0)
+    path = uv__basename_r(pathbuf);
+#endif
+  handle->cb(handle, path, events, 0);
+
+  if (handle->event_watcher.fd == -1)
+    return;
+
+  /* Watcher operates in one-shot mode, re-arm it. */
+  fflags = NOTE_ATTRIB | NOTE_WRITE  | NOTE_RENAME
+         | NOTE_DELETE | NOTE_EXTEND | NOTE_REVOKE;
+
+  EV_SET(&ev, w->fd, EVFILT_VNODE, EV_ADD | EV_ONESHOT, fflags, 0, 0);
+
+  if (kevent(loop->backend_fd, &ev, 1, NULL, 0, NULL))
+    abort();
+}
+
+
+int uv_fs_event_init(uv_loop_t* loop, uv_fs_event_t* handle) {
+  uv__handle_init(loop, (uv_handle_t*)handle, UV_FS_EVENT);
+  return 0;
+}
+
+
+int uv_fs_event_start(uv_fs_event_t* handle,
+                      uv_fs_event_cb cb,
+                      const char* path,
+                      unsigned int flags) {
+  int fd;
+#if defined(__APPLE__) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
+  struct stat statbuf;
+#endif
+
+  if (uv__is_active(handle))
+    return UV_EINVAL;
+
+  handle->cb = cb;
+  handle->path = uv__strdup(path);
+  if (handle->path == NULL)
+    return UV_ENOMEM;
+
+  /* TODO open asynchronously - but how do we report back errors? */
+  fd = open(handle->path, O_RDONLY);
+  if (fd == -1) {
+    uv__free(handle->path);
+    handle->path = NULL;
+    return UV__ERR(errno);
+  }
+
+#if defined(__APPLE__) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
+  /* Nullify field to perform checks later */
+  handle->cf_cb = NULL;
+  handle->realpath = NULL;
+  handle->realpath_len = 0;
+  handle->cf_flags = flags;
+
+  if (fstat(fd, &statbuf))
+    goto fallback;
+  /* FSEvents works only with directories */
+  if (!(statbuf.st_mode & S_IFDIR))
+    goto fallback;
+
+  if (0 == uv__load_relaxed(&uv__has_forked_with_cfrunloop)) {
+    int r;
+    /* The fallback fd is no longer needed */
+    uv__close_nocheckstdio(fd);
+    handle->event_watcher.fd = -1;
+    r = uv__fsevents_init(handle);
+    if (r == 0) {
+      uv__handle_start(handle);
+    } else {
+      uv__free(handle->path);
+      handle->path = NULL;
+    }
+    return r;
+  }
+fallback:
+#endif /* #if defined(__APPLE__) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 */
+
+  uv__handle_start(handle);
+  uv__io_init(&handle->event_watcher, uv__fs_event, fd);
+  uv__io_start(handle->loop, &handle->event_watcher, POLLIN);
+
+  return 0;
+}
+
+
+int uv_fs_event_stop(uv_fs_event_t* handle) {
+  int r;
+  r = 0;
+
+  if (!uv__is_active(handle))
+    return 0;
+
+  uv__handle_stop(handle);
+
+#if defined(__APPLE__) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
+  if (0 == uv__load_relaxed(&uv__has_forked_with_cfrunloop))
+    if (handle->cf_cb != NULL)
+      r = uv__fsevents_close(handle);
+#endif
+
+  if (handle->event_watcher.fd != -1) {
+    uv__io_close(handle->loop, &handle->event_watcher);
+    uv__close(handle->event_watcher.fd);
+    handle->event_watcher.fd = -1;
+  }
+
+  uv__free(handle->path);
+  handle->path = NULL;
+
+  return r;
+}
+
+
+void uv__fs_event_close(uv_fs_event_t* handle) {
+  uv_fs_event_stop(handle);
+}
diff --git a/wpinet/src/main/native/thirdparty/libuv/src/unix/linux-core.cpp b/wpinet/src/main/native/thirdparty/libuv/src/unix/linux-core.cpp
new file mode 100644
index 0000000..12ed7ff
--- /dev/null
+++ b/wpinet/src/main/native/thirdparty/libuv/src/unix/linux-core.cpp
@@ -0,0 +1,841 @@
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+/* We lean on the fact that POLL{IN,OUT,ERR,HUP} correspond with their
+ * EPOLL* counterparts.  We use the POLL* variants in this file because that
+ * is what libuv uses elsewhere.
+ */
+
+#include "uv.h"
+#include "internal.h"
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <errno.h>
+
+#include <net/if.h>
+#include <sys/epoll.h>
+#include <sys/param.h>
+#include <sys/prctl.h>
+#include <sys/sysinfo.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <time.h>
+
+#define HAVE_IFADDRS_H 1
+
+# if defined(__ANDROID_API__) && __ANDROID_API__ < 24
+# undef HAVE_IFADDRS_H
+#endif
+
+#ifdef __UCLIBC__
+# if __UCLIBC_MAJOR__ < 0 && __UCLIBC_MINOR__ < 9 && __UCLIBC_SUBLEVEL__ < 32
+#  undef HAVE_IFADDRS_H
+# endif
+#endif
+
+#ifdef HAVE_IFADDRS_H
+# include <ifaddrs.h>
+# include <sys/socket.h>
+# include <net/ethernet.h>
+# include <netpacket/packet.h>
+#endif /* HAVE_IFADDRS_H */
+
+/* Available from 2.6.32 onwards. */
+#ifndef CLOCK_MONOTONIC_COARSE
+# define CLOCK_MONOTONIC_COARSE 6
+#endif
+
+#ifdef __FRC_ROBORIO__
+#include "wpi/timestamp.h"
+#endif
+
+/* This is rather annoying: CLOCK_BOOTTIME lives in <linux/time.h> but we can't
+ * include that file because it conflicts with <time.h>. We'll just have to
+ * define it ourselves.
+ */
+#ifndef CLOCK_BOOTTIME
+# define CLOCK_BOOTTIME 7
+#endif
+
+static int read_models(unsigned int numcpus, uv_cpu_info_t* ci);
+static int read_times(FILE* statfile_fp,
+                      unsigned int numcpus,
+                      uv_cpu_info_t* ci);
+static void read_speeds(unsigned int numcpus, uv_cpu_info_t* ci);
+static uint64_t read_cpufreq(unsigned int cpunum);
+
+int uv__platform_loop_init(uv_loop_t* loop) {
+  
+  loop->inotify_fd = -1;
+  loop->inotify_watchers = NULL;
+
+  return uv__epoll_init(loop);
+}
+
+
+int uv__io_fork(uv_loop_t* loop) {
+  int err;
+  void* old_watchers;
+
+  old_watchers = loop->inotify_watchers;
+
+  uv__close(loop->backend_fd);
+  loop->backend_fd = -1;
+  uv__platform_loop_delete(loop);
+
+  err = uv__platform_loop_init(loop);
+  if (err)
+    return err;
+
+  return uv__inotify_fork(loop, old_watchers);
+}
+
+
+void uv__platform_loop_delete(uv_loop_t* loop) {
+  if (loop->inotify_fd == -1) return;
+  uv__io_stop(loop, &loop->inotify_read_watcher, POLLIN);
+  uv__close(loop->inotify_fd);
+  loop->inotify_fd = -1;
+}
+
+
+uint64_t uv__hrtime(uv_clocktype_t type) {
+#ifdef __FRC_ROBORIO__
+  return wpi::Now() * 1000u;
+#else
+  static clock_t fast_clock_id = -1;
+  struct timespec t;
+  clock_t clock_id;
+
+  /* Prefer CLOCK_MONOTONIC_COARSE if available but only when it has
+   * millisecond granularity or better.  CLOCK_MONOTONIC_COARSE is
+   * serviced entirely from the vDSO, whereas CLOCK_MONOTONIC may
+   * decide to make a costly system call.
+   */
+  /* TODO(bnoordhuis) Use CLOCK_MONOTONIC_COARSE for UV_CLOCK_PRECISE
+   * when it has microsecond granularity or better (unlikely).
+   */
+  clock_id = CLOCK_MONOTONIC;
+  if (type != UV_CLOCK_FAST)
+    goto done;
+
+  clock_id = uv__load_relaxed(&fast_clock_id);
+  if (clock_id != -1)
+    goto done;
+
+  clock_id = CLOCK_MONOTONIC;
+  if (0 == clock_getres(CLOCK_MONOTONIC_COARSE, &t))
+    if (t.tv_nsec <= 1 * 1000 * 1000)
+      clock_id = CLOCK_MONOTONIC_COARSE;
+
+  uv__store_relaxed(&fast_clock_id, clock_id);
+
+done:
+
+  if (clock_gettime(clock_id, &t))
+    return 0;  /* Not really possible. */
+
+  return t.tv_sec * (uint64_t) 1e9 + t.tv_nsec;
+#endif
+}
+
+
+int uv_resident_set_memory(size_t* rss) {
+  char buf[1024];
+  const char* s;
+  ssize_t n;
+  long val;
+  int fd;
+  int i;
+
+  do
+    fd = open("/proc/self/stat", O_RDONLY);
+  while (fd == -1 && errno == EINTR);
+
+  if (fd == -1)
+    return UV__ERR(errno);
+
+  do
+    n = read(fd, buf, sizeof(buf) - 1);
+  while (n == -1 && errno == EINTR);
+
+  uv__close(fd);
+  if (n == -1)
+    return UV__ERR(errno);
+  buf[n] = '\0';
+
+  s = strchr(buf, ' ');
+  if (s == NULL)
+    goto err;
+
+  s += 1;
+  if (*s != '(')
+    goto err;
+
+  s = strchr(s, ')');
+  if (s == NULL)
+    goto err;
+
+  for (i = 1; i <= 22; i++) {
+    s = strchr(s + 1, ' ');
+    if (s == NULL)
+      goto err;
+  }
+
+  errno = 0;
+  val = strtol(s, NULL, 10);
+  if (errno != 0)
+    goto err;
+  if (val < 0)
+    goto err;
+
+  *rss = val * getpagesize();
+  return 0;
+
+err:
+  return UV_EINVAL;
+}
+
+int uv_uptime(double* uptime) {
+  static volatile int no_clock_boottime;
+  char buf[128];
+  struct timespec now;
+  int r;
+
+  /* Try /proc/uptime first, then fallback to clock_gettime(). */
+
+  if (0 == uv__slurp("/proc/uptime", buf, sizeof(buf)))
+    if (1 == sscanf(buf, "%lf", uptime))
+      return 0;
+
+  /* Try CLOCK_BOOTTIME first, fall back to CLOCK_MONOTONIC if not available
+   * (pre-2.6.39 kernels). CLOCK_MONOTONIC doesn't increase when the system
+   * is suspended.
+   */
+  if (no_clock_boottime) {
+    retry_clock_gettime: r = clock_gettime(CLOCK_MONOTONIC, &now);
+  }
+  else if ((r = clock_gettime(CLOCK_BOOTTIME, &now)) && errno == EINVAL) {
+    no_clock_boottime = 1;
+    goto retry_clock_gettime;
+  }
+
+  if (r)
+    return UV__ERR(errno);
+
+  *uptime = now.tv_sec;
+  return 0;
+}
+
+
+static int uv__cpu_num(FILE* statfile_fp, unsigned int* numcpus) {
+  unsigned int num;
+  char buf[1024];
+
+  if (!fgets(buf, sizeof(buf), statfile_fp))
+    return UV_EIO;
+
+  num = 0;
+  while (fgets(buf, sizeof(buf), statfile_fp)) {
+    if (strncmp(buf, "cpu", 3))
+      break;
+    num++;
+  }
+
+  if (num == 0)
+    return UV_EIO;
+
+  *numcpus = num;
+  return 0;
+}
+
+
+int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) {
+  unsigned int numcpus;
+  uv_cpu_info_t* ci;
+  int err;
+  FILE* statfile_fp;
+
+  *cpu_infos = NULL;
+  *count = 0;
+
+  statfile_fp = uv__open_file("/proc/stat");
+  if (statfile_fp == NULL)
+    return UV__ERR(errno);
+
+  err = uv__cpu_num(statfile_fp, &numcpus);
+  if (err < 0)
+    goto out;
+
+  err = UV_ENOMEM;
+  ci = (uv_cpu_info_t*)uv__calloc(numcpus, sizeof(*ci));
+  if (ci == NULL)
+    goto out;
+
+  err = read_models(numcpus, ci);
+  if (err == 0)
+    err = read_times(statfile_fp, numcpus, ci);
+
+  if (err) {
+    uv_free_cpu_info(ci, numcpus);
+    goto out;
+  }
+
+  /* read_models() on x86 also reads the CPU speed from /proc/cpuinfo.
+   * We don't check for errors here. Worst case, the field is left zero.
+   */
+  if (ci[0].speed == 0)
+    read_speeds(numcpus, ci);
+
+  *cpu_infos = ci;
+  *count = numcpus;
+  err = 0;
+
+out:
+
+  if (fclose(statfile_fp))
+    if (errno != EINTR && errno != EINPROGRESS)
+      abort();
+
+  return err;
+}
+
+
+static void read_speeds(unsigned int numcpus, uv_cpu_info_t* ci) {
+  unsigned int num;
+
+  for (num = 0; num < numcpus; num++)
+    ci[num].speed = read_cpufreq(num) / 1000;
+}
+
+
+/* Also reads the CPU frequency on ppc and x86. The other architectures only
+ * have a BogoMIPS field, which may not be very accurate.
+ *
+ * Note: Simply returns on error, uv_cpu_info() takes care of the cleanup.
+ */
+static int read_models(unsigned int numcpus, uv_cpu_info_t* ci) {
+#if defined(__PPC__)
+  static const char model_marker[] = "cpu\t\t: ";
+  static const char speed_marker[] = "clock\t\t: ";
+#else
+  static const char model_marker[] = "model name\t: ";
+  static const char speed_marker[] = "cpu MHz\t\t: ";
+#endif
+  const char* inferred_model;
+  unsigned int model_idx;
+  unsigned int speed_idx;
+  unsigned int part_idx;
+  char buf[1024];
+  char* model;
+  FILE* fp;
+  int model_id;
+
+  /* Most are unused on non-ARM, non-MIPS and non-x86 architectures. */
+  (void) &model_marker;
+  (void) &speed_marker;
+  (void) &speed_idx;
+  (void) &part_idx;
+  (void) &model;
+  (void) &buf;
+  (void) &fp;
+  (void) &model_id;
+
+  model_idx = 0;
+  speed_idx = 0;
+  part_idx = 0;
+
+#if defined(__arm__) || \
+    defined(__i386__) || \
+    defined(__mips__) || \
+    defined(__aarch64__) || \
+    defined(__PPC__) || \
+    defined(__x86_64__)
+  fp = uv__open_file("/proc/cpuinfo");
+  if (fp == NULL)
+    return UV__ERR(errno);
+
+  while (fgets(buf, sizeof(buf), fp)) {
+    if (model_idx < numcpus) {
+      if (strncmp(buf, model_marker, sizeof(model_marker) - 1) == 0) {
+        model = buf + sizeof(model_marker) - 1;
+        model = uv__strndup(model, strlen(model) - 1);  /* Strip newline. */
+        if (model == NULL) {
+          fclose(fp);
+          return UV_ENOMEM;
+        }
+        ci[model_idx++].model = model;
+        continue;
+      }
+    }
+#if defined(__arm__) || defined(__mips__) || defined(__aarch64__)
+    if (model_idx < numcpus) {
+#if defined(__arm__)
+      /* Fallback for pre-3.8 kernels. */
+      static const char model_marker[] = "Processor\t: ";
+#elif defined(__aarch64__)
+      static const char part_marker[] = "CPU part\t: ";
+
+      /* Adapted from: https://github.com/karelzak/util-linux */
+      struct vendor_part {
+        const int id;
+        const char* name;
+      };
+
+      static const struct vendor_part arm_chips[] = {
+        { 0x811, "ARM810" },
+        { 0x920, "ARM920" },
+        { 0x922, "ARM922" },
+        { 0x926, "ARM926" },
+        { 0x940, "ARM940" },
+        { 0x946, "ARM946" },
+        { 0x966, "ARM966" },
+        { 0xa20, "ARM1020" },
+        { 0xa22, "ARM1022" },
+        { 0xa26, "ARM1026" },
+        { 0xb02, "ARM11 MPCore" },
+        { 0xb36, "ARM1136" },
+        { 0xb56, "ARM1156" },
+        { 0xb76, "ARM1176" },
+        { 0xc05, "Cortex-A5" },
+        { 0xc07, "Cortex-A7" },
+        { 0xc08, "Cortex-A8" },
+        { 0xc09, "Cortex-A9" },
+        { 0xc0d, "Cortex-A17" },  /* Originally A12 */
+        { 0xc0f, "Cortex-A15" },
+        { 0xc0e, "Cortex-A17" },
+        { 0xc14, "Cortex-R4" },
+        { 0xc15, "Cortex-R5" },
+        { 0xc17, "Cortex-R7" },
+        { 0xc18, "Cortex-R8" },
+        { 0xc20, "Cortex-M0" },
+        { 0xc21, "Cortex-M1" },
+        { 0xc23, "Cortex-M3" },
+        { 0xc24, "Cortex-M4" },
+        { 0xc27, "Cortex-M7" },
+        { 0xc60, "Cortex-M0+" },
+        { 0xd01, "Cortex-A32" },
+        { 0xd03, "Cortex-A53" },
+        { 0xd04, "Cortex-A35" },
+        { 0xd05, "Cortex-A55" },
+        { 0xd06, "Cortex-A65" },
+        { 0xd07, "Cortex-A57" },
+        { 0xd08, "Cortex-A72" },
+        { 0xd09, "Cortex-A73" },
+        { 0xd0a, "Cortex-A75" },
+        { 0xd0b, "Cortex-A76" },
+        { 0xd0c, "Neoverse-N1" },
+        { 0xd0d, "Cortex-A77" },
+        { 0xd0e, "Cortex-A76AE" },
+        { 0xd13, "Cortex-R52" },
+        { 0xd20, "Cortex-M23" },
+        { 0xd21, "Cortex-M33" },
+        { 0xd41, "Cortex-A78" },
+        { 0xd42, "Cortex-A78AE" },
+        { 0xd4a, "Neoverse-E1" },
+        { 0xd4b, "Cortex-A78C" },
+      };
+
+      if (strncmp(buf, part_marker, sizeof(part_marker) - 1) == 0) {
+        model = buf + sizeof(part_marker) - 1;
+
+        errno = 0;
+        model_id = strtol(model, NULL, 16);
+        if ((errno != 0) || model_id < 0) {
+          fclose(fp);
+          return UV_EINVAL;
+        }
+
+        for (part_idx = 0; part_idx < ARRAY_SIZE(arm_chips); part_idx++) {
+          if (model_id == arm_chips[part_idx].id) {
+            model = uv__strdup(arm_chips[part_idx].name);
+            if (model == NULL) {
+              fclose(fp);
+              return UV_ENOMEM;
+            }
+            ci[model_idx++].model = model;
+            break;
+          }
+        }
+      }
+#else	/* defined(__mips__) */
+      static const char model_marker[] = "cpu model\t\t: ";
+#endif
+      if (strncmp(buf, model_marker, sizeof(model_marker) - 1) == 0) {
+        model = buf + sizeof(model_marker) - 1;
+        model = uv__strndup(model, strlen(model) - 1);  /* Strip newline. */
+        if (model == NULL) {
+          fclose(fp);
+          return UV_ENOMEM;
+        }
+        ci[model_idx++].model = model;
+        continue;
+      }
+    }
+#else  /* !__arm__ && !__mips__ && !__aarch64__ */
+    if (speed_idx < numcpus) {
+      if (strncmp(buf, speed_marker, sizeof(speed_marker) - 1) == 0) {
+        ci[speed_idx++].speed = atoi(buf + sizeof(speed_marker) - 1);
+        continue;
+      }
+    }
+#endif  /* __arm__ || __mips__ || __aarch64__ */
+  }
+
+  fclose(fp);
+#endif  /* __arm__ || __i386__ || __mips__ || __PPC__ || __x86_64__ || __aarch__ */
+
+  /* Now we want to make sure that all the models contain *something* because
+   * it's not safe to leave them as null. Copy the last entry unless there
+   * isn't one, in that case we simply put "unknown" into everything.
+   */
+  inferred_model = "unknown";
+  if (model_idx > 0)
+    inferred_model = ci[model_idx - 1].model;
+
+  while (model_idx < numcpus) {
+    model = uv__strndup(inferred_model, strlen(inferred_model));
+    if (model == NULL)
+      return UV_ENOMEM;
+    ci[model_idx++].model = model;
+  }
+
+  return 0;
+}
+
+
+static int read_times(FILE* statfile_fp,
+                      unsigned int numcpus,
+                      uv_cpu_info_t* ci) {
+  struct uv_cpu_times_s ts;
+  unsigned int ticks;
+  unsigned int multiplier;
+  uint64_t user;
+  uint64_t nice;
+  uint64_t sys;
+  uint64_t idle;
+  uint64_t dummy;
+  uint64_t irq;
+  uint64_t num;
+  uint64_t len;
+  char buf[1024];
+
+  ticks = (unsigned int)sysconf(_SC_CLK_TCK);
+  assert(ticks != (unsigned int) -1);
+  assert(ticks != 0);
+  multiplier = ((uint64_t)1000L / ticks);
+
+  rewind(statfile_fp);
+
+  if (!fgets(buf, sizeof(buf), statfile_fp))
+    abort();
+
+  num = 0;
+
+  while (fgets(buf, sizeof(buf), statfile_fp)) {
+    if (num >= numcpus)
+      break;
+
+    if (strncmp(buf, "cpu", 3))
+      break;
+
+    /* skip "cpu<num> " marker */
+    {
+      unsigned int n;
+      int r = sscanf(buf, "cpu%u ", &n);
+      assert(r == 1);
+      (void) r;  /* silence build warning */
+      for (len = sizeof("cpu0"); n /= 10; len++);
+    }
+
+    /* Line contains user, nice, system, idle, iowait, irq, softirq, steal,
+     * guest, guest_nice but we're only interested in the first four + irq.
+     *
+     * Don't use %*s to skip fields or %ll to read straight into the uint64_t
+     * fields, they're not allowed in C89 mode.
+     */
+    if (6 != sscanf(buf + len,
+                    "%" PRIu64 " %" PRIu64 " %" PRIu64
+                    "%" PRIu64 " %" PRIu64 " %" PRIu64,
+                    &user,
+                    &nice,
+                    &sys,
+                    &idle,
+                    &dummy,
+                    &irq))
+      abort();
+
+    ts.user = user * multiplier;
+    ts.nice = nice * multiplier;
+    ts.sys  = sys * multiplier;
+    ts.idle = idle * multiplier;
+    ts.irq  = irq * multiplier;
+    ci[num++].cpu_times = ts;
+  }
+  assert(num == numcpus);
+
+  return 0;
+}
+
+
+static uint64_t read_cpufreq(unsigned int cpunum) {
+  uint64_t val;
+  char buf[1024];
+  FILE* fp;
+
+  snprintf(buf,
+           sizeof(buf),
+           "/sys/devices/system/cpu/cpu%u/cpufreq/scaling_cur_freq",
+           cpunum);
+
+  fp = uv__open_file(buf);
+  if (fp == NULL)
+    return 0;
+
+  if (fscanf(fp, "%" PRIu64, &val) != 1)
+    val = 0;
+
+  fclose(fp);
+
+  return val;
+}
+
+
+#ifdef HAVE_IFADDRS_H
+static int uv__ifaddr_exclude(struct ifaddrs *ent, int exclude_type) {
+  if (!((ent->ifa_flags & IFF_UP) && (ent->ifa_flags & IFF_RUNNING)))
+    return 1;
+  if (ent->ifa_addr == NULL)
+    return 1;
+  /*
+   * On Linux getifaddrs returns information related to the raw underlying
+   * devices. We're not interested in this information yet.
+   */
+  if (ent->ifa_addr->sa_family == PF_PACKET)
+    return exclude_type;
+  return !exclude_type;
+}
+#endif
+
+int uv_interface_addresses(uv_interface_address_t** addresses, int* count) {
+#ifndef HAVE_IFADDRS_H
+  *count = 0;
+  *addresses = NULL;
+  return UV_ENOSYS;
+#else
+  struct ifaddrs *addrs, *ent;
+  uv_interface_address_t* address;
+  int i;
+  struct sockaddr_ll *sll;
+
+  *count = 0;
+  *addresses = NULL;
+
+  if (getifaddrs(&addrs))
+    return UV__ERR(errno);
+
+  /* Count the number of interfaces */
+  for (ent = addrs; ent != NULL; ent = ent->ifa_next) {
+    if (uv__ifaddr_exclude(ent, UV__EXCLUDE_IFADDR))
+      continue;
+
+    (*count)++;
+  }
+
+  if (*count == 0) {
+    freeifaddrs(addrs);
+    return 0;
+  }
+
+  /* Make sure the memory is initiallized to zero using calloc() */
+  *addresses = (uv_interface_address_t*)uv__calloc(*count, sizeof(**addresses));
+  if (!(*addresses)) {
+    freeifaddrs(addrs);
+    return UV_ENOMEM;
+  }
+
+  address = *addresses;
+
+  for (ent = addrs; ent != NULL; ent = ent->ifa_next) {
+    if (uv__ifaddr_exclude(ent, UV__EXCLUDE_IFADDR))
+      continue;
+
+    address->name = uv__strdup(ent->ifa_name);
+
+    if (ent->ifa_addr->sa_family == AF_INET6) {
+      address->address.address6 = *((struct sockaddr_in6*) ent->ifa_addr);
+    } else {
+      address->address.address4 = *((struct sockaddr_in*) ent->ifa_addr);
+    }
+
+    if (ent->ifa_netmask->sa_family == AF_INET6) {
+      address->netmask.netmask6 = *((struct sockaddr_in6*) ent->ifa_netmask);
+    } else {
+      address->netmask.netmask4 = *((struct sockaddr_in*) ent->ifa_netmask);
+    }
+
+    address->is_internal = !!(ent->ifa_flags & IFF_LOOPBACK);
+
+    address++;
+  }
+
+  /* Fill in physical addresses for each interface */
+  for (ent = addrs; ent != NULL; ent = ent->ifa_next) {
+    if (uv__ifaddr_exclude(ent, UV__EXCLUDE_IFPHYS))
+      continue;
+
+    address = *addresses;
+
+    for (i = 0; i < (*count); i++) {
+      size_t namelen = strlen(ent->ifa_name);
+      /* Alias interface share the same physical address */
+      if (strncmp(address->name, ent->ifa_name, namelen) == 0 &&
+          (address->name[namelen] == 0 || address->name[namelen] == ':')) {
+        sll = (struct sockaddr_ll*)ent->ifa_addr;
+        memcpy(address->phys_addr, sll->sll_addr, sizeof(address->phys_addr));
+      }
+      address++;
+    }
+  }
+
+  freeifaddrs(addrs);
+
+  return 0;
+#endif
+}
+
+
+void uv_free_interface_addresses(uv_interface_address_t* addresses,
+  int count) {
+  int i;
+
+  for (i = 0; i < count; i++) {
+    uv__free(addresses[i].name);
+  }
+
+  uv__free(addresses);
+}
+
+
+void uv__set_process_title(const char* title) {
+#if defined(PR_SET_NAME)
+  prctl(PR_SET_NAME, title);  /* Only copies first 16 characters. */
+#endif
+}
+
+
+static uint64_t uv__read_proc_meminfo(const char* what) {
+  uint64_t rc;
+  char* p;
+  char buf[4096];  /* Large enough to hold all of /proc/meminfo. */
+
+  if (uv__slurp("/proc/meminfo", buf, sizeof(buf)))
+    return 0;
+
+  p = strstr(buf, what);
+
+  if (p == NULL)
+    return 0;
+
+  p += strlen(what);
+
+  rc = 0;
+  sscanf(p, "%" PRIu64 " kB", &rc);
+
+  return rc * 1024;
+}
+
+
+uint64_t uv_get_free_memory(void) {
+  struct sysinfo info;
+  uint64_t rc;
+
+  rc = uv__read_proc_meminfo("MemAvailable:");
+
+  if (rc != 0)
+    return rc;
+
+  if (0 == sysinfo(&info))
+    return (uint64_t) info.freeram * info.mem_unit;
+
+  return 0;
+}
+
+
+uint64_t uv_get_total_memory(void) {
+  struct sysinfo info;
+  uint64_t rc;
+
+  rc = uv__read_proc_meminfo("MemTotal:");
+
+  if (rc != 0)
+    return rc;
+
+  if (0 == sysinfo(&info))
+    return (uint64_t) info.totalram * info.mem_unit;
+
+  return 0;
+}
+
+
+static uint64_t uv__read_cgroups_uint64(const char* cgroup, const char* param) {
+  char filename[256];
+  char buf[32];  /* Large enough to hold an encoded uint64_t. */
+  uint64_t rc;
+
+  rc = 0;
+  snprintf(filename, sizeof(filename), "/sys/fs/cgroup/%s/%s", cgroup, param);
+  if (0 == uv__slurp(filename, buf, sizeof(buf)))
+    sscanf(buf, "%" PRIu64, &rc);
+
+  return rc;
+}
+
+
+uint64_t uv_get_constrained_memory(void) {
+  /*
+   * This might return 0 if there was a problem getting the memory limit from
+   * cgroups. This is OK because a return value of 0 signifies that the memory
+   * limit is unknown.
+   */
+  return uv__read_cgroups_uint64("memory", "memory.limit_in_bytes");
+}
+
+
+void uv_loadavg(double avg[3]) {
+  struct sysinfo info;
+  char buf[128];  /* Large enough to hold all of /proc/loadavg. */
+
+  if (0 == uv__slurp("/proc/loadavg", buf, sizeof(buf)))
+    if (3 == sscanf(buf, "%lf %lf %lf", &avg[0], &avg[1], &avg[2]))
+      return;
+
+  if (sysinfo(&info) < 0)
+    return;
+
+  avg[0] = (double) info.loads[0] / 65536.0;
+  avg[1] = (double) info.loads[1] / 65536.0;
+  avg[2] = (double) info.loads[2] / 65536.0;
+}
diff --git a/wpinet/src/main/native/thirdparty/libuv/src/unix/linux-inotify.cpp b/wpinet/src/main/native/thirdparty/libuv/src/unix/linux-inotify.cpp
new file mode 100644
index 0000000..f5366e9
--- /dev/null
+++ b/wpinet/src/main/native/thirdparty/libuv/src/unix/linux-inotify.cpp
@@ -0,0 +1,327 @@
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include "uv.h"
+#include "uv/tree.h"
+#include "internal.h"
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <errno.h>
+
+#include <sys/inotify.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+struct watcher_list {
+  RB_ENTRY(watcher_list) entry;
+  QUEUE watchers;
+  int iterating;
+  char* path;
+  int wd;
+};
+
+struct watcher_root {
+  struct watcher_list* rbh_root;
+};
+#define CAST(p) ((struct watcher_root*)(p))
+
+
+static int compare_watchers(const struct watcher_list* a,
+                            const struct watcher_list* b) {
+  if (a->wd < b->wd) return -1;
+  if (a->wd > b->wd) return 1;
+  return 0;
+}
+
+
+RB_GENERATE_STATIC(watcher_root, watcher_list, entry, compare_watchers)
+
+
+static void uv__inotify_read(uv_loop_t* loop,
+                             uv__io_t* w,
+                             unsigned int revents);
+
+static void maybe_free_watcher_list(struct watcher_list* w,
+                                    uv_loop_t* loop);
+
+static int init_inotify(uv_loop_t* loop) {
+  int fd;
+
+  if (loop->inotify_fd != -1)
+    return 0;
+
+  fd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
+  if (fd < 0)
+    return UV__ERR(errno);
+
+  loop->inotify_fd = fd;
+  uv__io_init(&loop->inotify_read_watcher, uv__inotify_read, loop->inotify_fd);
+  uv__io_start(loop, &loop->inotify_read_watcher, POLLIN);
+
+  return 0;
+}
+
+
+int uv__inotify_fork(uv_loop_t* loop, void* old_watchers) {
+  /* Open the inotify_fd, and re-arm all the inotify watchers. */
+  int err;
+  struct watcher_list* tmp_watcher_list_iter;
+  struct watcher_list* watcher_list;
+  struct watcher_list tmp_watcher_list;
+  QUEUE queue;
+  QUEUE* q;
+  uv_fs_event_t* handle;
+  char* tmp_path;
+
+  if (old_watchers != NULL) {
+    /* We must restore the old watcher list to be able to close items
+     * out of it.
+     */
+    loop->inotify_watchers = old_watchers;
+
+    QUEUE_INIT(&tmp_watcher_list.watchers);
+    /* Note that the queue we use is shared with the start and stop()
+     * functions, making QUEUE_FOREACH unsafe to use. So we use the
+     * QUEUE_MOVE trick to safely iterate. Also don't free the watcher
+     * list until we're done iterating. c.f. uv__inotify_read.
+     */
+    RB_FOREACH_SAFE(watcher_list, watcher_root,
+                    CAST(&old_watchers), tmp_watcher_list_iter) {
+      watcher_list->iterating = 1;
+      QUEUE_MOVE(&watcher_list->watchers, &queue);
+      while (!QUEUE_EMPTY(&queue)) {
+        q = QUEUE_HEAD(&queue);
+        handle = QUEUE_DATA(q, uv_fs_event_t, watchers);
+        /* It's critical to keep a copy of path here, because it
+         * will be set to NULL by stop() and then deallocated by
+         * maybe_free_watcher_list
+         */
+        tmp_path = uv__strdup(handle->path);
+        assert(tmp_path != NULL);
+        QUEUE_REMOVE(q);
+        QUEUE_INSERT_TAIL(&watcher_list->watchers, q);
+        uv_fs_event_stop(handle);
+
+        QUEUE_INSERT_TAIL(&tmp_watcher_list.watchers, &handle->watchers);
+        handle->path = tmp_path;
+      }
+      watcher_list->iterating = 0;
+      maybe_free_watcher_list(watcher_list, loop);
+    }
+
+    QUEUE_MOVE(&tmp_watcher_list.watchers, &queue);
+    while (!QUEUE_EMPTY(&queue)) {
+        q = QUEUE_HEAD(&queue);
+        QUEUE_REMOVE(q);
+        handle = QUEUE_DATA(q, uv_fs_event_t, watchers);
+        tmp_path = handle->path;
+        handle->path = NULL;
+        err = uv_fs_event_start(handle, handle->cb, tmp_path, 0);
+        uv__free(tmp_path);
+        if (err)
+          return err;
+    }
+  }
+
+  return 0;
+}
+
+
+static struct watcher_list* find_watcher(uv_loop_t* loop, int wd) {
+  struct watcher_list w;
+  w.wd = wd;
+  return RB_FIND(watcher_root, CAST(&loop->inotify_watchers), &w);
+}
+
+static void maybe_free_watcher_list(struct watcher_list* w, uv_loop_t* loop) {
+  /* if the watcher_list->watchers is being iterated over, we can't free it. */
+  if ((!w->iterating) && QUEUE_EMPTY(&w->watchers)) {
+    /* No watchers left for this path. Clean up. */
+    RB_REMOVE(watcher_root, CAST(&loop->inotify_watchers), w);
+    inotify_rm_watch(loop->inotify_fd, w->wd);
+    uv__free(w);
+  }
+}
+
+static void uv__inotify_read(uv_loop_t* loop,
+                             uv__io_t* dummy,
+                             unsigned int events) {
+  const struct inotify_event* e;
+  struct watcher_list* w;
+  uv_fs_event_t* h;
+  QUEUE queue;
+  QUEUE* q;
+  const char* path;
+  ssize_t size;
+  const char *p;
+  /* needs to be large enough for sizeof(inotify_event) + strlen(path) */
+  char buf[4096];
+
+  for (;;) {
+    do
+      size = read(loop->inotify_fd, buf, sizeof(buf));
+    while (size == -1 && errno == EINTR);
+
+    if (size == -1) {
+      assert(errno == EAGAIN || errno == EWOULDBLOCK);
+      break;
+    }
+
+    assert(size > 0); /* pre-2.6.21 thing, size=0 == read buffer too small */
+
+    /* Now we have one or more inotify_event structs. */
+    for (p = buf; p < buf + size; p += sizeof(*e) + e->len) {
+      e = (const struct inotify_event*) p;
+
+      events = 0;
+      if (e->mask & (IN_ATTRIB|IN_MODIFY))
+        events |= UV_CHANGE;
+      if (e->mask & ~(IN_ATTRIB|IN_MODIFY))
+        events |= UV_RENAME;
+
+      w = find_watcher(loop, e->wd);
+      if (w == NULL)
+        continue; /* Stale event, no watchers left. */
+
+      /* inotify does not return the filename when monitoring a single file
+       * for modifications. Repurpose the filename for API compatibility.
+       * I'm not convinced this is a good thing, maybe it should go.
+       */
+      path = e->len ? (const char*) (e + 1) : uv__basename_r(w->path);
+
+      /* We're about to iterate over the queue and call user's callbacks.
+       * What can go wrong?
+       * A callback could call uv_fs_event_stop()
+       * and the queue can change under our feet.
+       * So, we use QUEUE_MOVE() trick to safely iterate over the queue.
+       * And we don't free the watcher_list until we're done iterating.
+       *
+       * First,
+       * tell uv_fs_event_stop() (that could be called from a user's callback)
+       * not to free watcher_list.
+       */
+      w->iterating = 1;
+      QUEUE_MOVE(&w->watchers, &queue);
+      while (!QUEUE_EMPTY(&queue)) {
+        q = QUEUE_HEAD(&queue);
+        h = QUEUE_DATA(q, uv_fs_event_t, watchers);
+
+        QUEUE_REMOVE(q);
+        QUEUE_INSERT_TAIL(&w->watchers, q);
+
+        h->cb(h, path, events, 0);
+      }
+      /* done iterating, time to (maybe) free empty watcher_list */
+      w->iterating = 0;
+      maybe_free_watcher_list(w, loop);
+    }
+  }
+}
+
+
+int uv_fs_event_init(uv_loop_t* loop, uv_fs_event_t* handle) {
+  uv__handle_init(loop, (uv_handle_t*)handle, UV_FS_EVENT);
+  return 0;
+}
+
+
+int uv_fs_event_start(uv_fs_event_t* handle,
+                      uv_fs_event_cb cb,
+                      const char* path,
+                      unsigned int flags) {
+  struct watcher_list* w;
+  size_t len;
+  int events;
+  int err;
+  int wd;
+
+  if (uv__is_active(handle))
+    return UV_EINVAL;
+
+  err = init_inotify(handle->loop);
+  if (err)
+    return err;
+
+  events = IN_ATTRIB
+         | IN_CREATE
+         | IN_MODIFY
+         | IN_DELETE
+         | IN_DELETE_SELF
+         | IN_MOVE_SELF
+         | IN_MOVED_FROM
+         | IN_MOVED_TO;
+
+  wd = inotify_add_watch(handle->loop->inotify_fd, path, events);
+  if (wd == -1)
+    return UV__ERR(errno);
+
+  w = find_watcher(handle->loop, wd);
+  if (w)
+    goto no_insert;
+
+  len = strlen(path) + 1;
+  w = (watcher_list*)uv__malloc(sizeof(*w) + len);
+  if (w == NULL)
+    return UV_ENOMEM;
+
+  w->wd = wd;
+  w->path = (char*)memcpy(w + 1, path, len);
+  QUEUE_INIT(&w->watchers);
+  w->iterating = 0;
+  RB_INSERT(watcher_root, CAST(&handle->loop->inotify_watchers), w);
+
+no_insert:
+  uv__handle_start(handle);
+  QUEUE_INSERT_TAIL(&w->watchers, &handle->watchers);
+  handle->path = w->path;
+  handle->cb = cb;
+  handle->wd = wd;
+
+  return 0;
+}
+
+
+int uv_fs_event_stop(uv_fs_event_t* handle) {
+  struct watcher_list* w;
+
+  if (!uv__is_active(handle))
+    return 0;
+
+  w = find_watcher(handle->loop, handle->wd);
+  assert(w != NULL);
+
+  handle->wd = -1;
+  handle->path = NULL;
+  uv__handle_stop(handle);
+  QUEUE_REMOVE(&handle->watchers);
+
+  maybe_free_watcher_list(w, handle->loop);
+
+  return 0;
+}
+
+
+void uv__fs_event_close(uv_fs_event_t* handle) {
+  uv_fs_event_stop(handle);
+}
diff --git a/wpinet/src/main/native/thirdparty/libuv/src/unix/linux-syscalls.cpp b/wpinet/src/main/native/thirdparty/libuv/src/unix/linux-syscalls.cpp
new file mode 100644
index 0000000..5071cd5
--- /dev/null
+++ b/wpinet/src/main/native/thirdparty/libuv/src/unix/linux-syscalls.cpp
@@ -0,0 +1,264 @@
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include "linux-syscalls.h"
+#include <unistd.h>
+#include <signal.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <errno.h>
+
+#if defined(__arm__)
+# if defined(__thumb__) || defined(__ARM_EABI__)
+#  define UV_SYSCALL_BASE 0
+# else
+#  define UV_SYSCALL_BASE 0x900000
+# endif
+#endif /* __arm__ */
+
+#ifndef __NR_recvmmsg
+# if defined(__x86_64__)
+#  define __NR_recvmmsg 299
+# elif defined(__arm__)
+#  define __NR_recvmmsg (UV_SYSCALL_BASE + 365)
+# endif
+#endif /* __NR_recvmsg */
+
+#ifndef __NR_sendmmsg
+# if defined(__x86_64__)
+#  define __NR_sendmmsg 307
+# elif defined(__arm__)
+#  define __NR_sendmmsg (UV_SYSCALL_BASE + 374)
+# endif
+#endif /* __NR_sendmmsg */
+
+#ifndef __NR_utimensat
+# if defined(__x86_64__)
+#  define __NR_utimensat 280
+# elif defined(__i386__)
+#  define __NR_utimensat 320
+# elif defined(__arm__)
+#  define __NR_utimensat (UV_SYSCALL_BASE + 348)
+# endif
+#endif /* __NR_utimensat */
+
+#ifndef __NR_preadv
+# if defined(__x86_64__)
+#  define __NR_preadv 295
+# elif defined(__i386__)
+#  define __NR_preadv 333
+# elif defined(__arm__)
+#  define __NR_preadv (UV_SYSCALL_BASE + 361)
+# endif
+#endif /* __NR_preadv */
+
+#ifndef __NR_pwritev
+# if defined(__x86_64__)
+#  define __NR_pwritev 296
+# elif defined(__i386__)
+#  define __NR_pwritev 334
+# elif defined(__arm__)
+#  define __NR_pwritev (UV_SYSCALL_BASE + 362)
+# endif
+#endif /* __NR_pwritev */
+
+#ifndef __NR_dup3
+# if defined(__x86_64__)
+#  define __NR_dup3 292
+# elif defined(__i386__)
+#  define __NR_dup3 330
+# elif defined(__arm__)
+#  define __NR_dup3 (UV_SYSCALL_BASE + 358)
+# endif
+#endif /* __NR_pwritev */
+
+#ifndef __NR_copy_file_range
+# if defined(__x86_64__)
+#  define __NR_copy_file_range 326
+# elif defined(__i386__)
+#  define __NR_copy_file_range 377
+# elif defined(__s390__)
+#  define __NR_copy_file_range 375
+# elif defined(__arm__)
+#  define __NR_copy_file_range (UV_SYSCALL_BASE + 391)
+# elif defined(__aarch64__)
+#  define __NR_copy_file_range 285
+# elif defined(__powerpc__)
+#  define __NR_copy_file_range 379
+# elif defined(__arc__)
+#  define __NR_copy_file_range 285
+# endif
+#endif /* __NR_copy_file_range */
+
+#ifndef __NR_statx
+# if defined(__x86_64__)
+#  define __NR_statx 332
+# elif defined(__i386__)
+#  define __NR_statx 383
+# elif defined(__aarch64__)
+#  define __NR_statx 397
+# elif defined(__arm__)
+#  define __NR_statx (UV_SYSCALL_BASE + 397)
+# elif defined(__ppc__)
+#  define __NR_statx 383
+# elif defined(__s390__)
+#  define __NR_statx 379
+# endif
+#endif /* __NR_statx */
+
+#ifndef __NR_getrandom
+# if defined(__x86_64__)
+#  define __NR_getrandom 318
+# elif defined(__i386__)
+#  define __NR_getrandom 355
+# elif defined(__aarch64__)
+#  define __NR_getrandom 384
+# elif defined(__arm__)
+#  define __NR_getrandom (UV_SYSCALL_BASE + 384)
+# elif defined(__ppc__)
+#  define __NR_getrandom 359
+# elif defined(__s390__)
+#  define __NR_getrandom 349
+# endif
+#endif /* __NR_getrandom */
+
+struct uv__mmsghdr;
+
+int uv__sendmmsg(int fd, struct uv__mmsghdr* mmsg, unsigned int vlen) {
+#if defined(__i386__)
+  unsigned long args[4];
+  int rc;
+
+  args[0] = (unsigned long) fd;
+  args[1] = (unsigned long) mmsg;
+  args[2] = (unsigned long) vlen;
+  args[3] = /* flags */ 0;
+
+  /* socketcall() raises EINVAL when SYS_SENDMMSG is not supported. */
+  rc = syscall(/* __NR_socketcall */ 102, 20 /* SYS_SENDMMSG */, args);
+  if (rc == -1)
+    if (errno == EINVAL)
+      errno = ENOSYS;
+
+  return rc;
+#elif defined(__NR_sendmmsg)
+  return syscall(__NR_sendmmsg, fd, mmsg, vlen, /* flags */ 0);
+#else
+  return errno = ENOSYS, -1;
+#endif
+}
+
+
+int uv__recvmmsg(int fd, struct uv__mmsghdr* mmsg, unsigned int vlen) {
+#if defined(__i386__)
+  unsigned long args[5];
+  int rc;
+
+  args[0] = (unsigned long) fd;
+  args[1] = (unsigned long) mmsg;
+  args[2] = (unsigned long) vlen;
+  args[3] = /* flags */ 0;
+  args[4] = /* timeout */ 0;
+
+  /* socketcall() raises EINVAL when SYS_RECVMMSG is not supported. */
+  rc = syscall(/* __NR_socketcall */ 102, 19 /* SYS_RECVMMSG */, args);
+  if (rc == -1)
+    if (errno == EINVAL)
+      errno = ENOSYS;
+
+  return rc;
+#elif defined(__NR_recvmmsg)
+  return syscall(__NR_recvmmsg, fd, mmsg, vlen, /* flags */ 0, /* timeout */ 0);
+#else
+  return errno = ENOSYS, -1;
+#endif
+}
+
+
+ssize_t uv__preadv(int fd, const struct iovec *iov, int iovcnt, int64_t offset) {
+#if !defined(__NR_preadv) || defined(__ANDROID_API__) && __ANDROID_API__ < 24
+  return errno = ENOSYS, -1;
+#else
+  return syscall(__NR_preadv, fd, iov, iovcnt, (long)offset, (long)(offset >> 32));
+#endif
+}
+
+
+ssize_t uv__pwritev(int fd, const struct iovec *iov, int iovcnt, int64_t offset) {
+#if !defined(__NR_pwritev) || defined(__ANDROID_API__) && __ANDROID_API__ < 24
+  return errno = ENOSYS, -1;
+#else
+  return syscall(__NR_pwritev, fd, iov, iovcnt, (long)offset, (long)(offset >> 32));
+#endif
+}
+
+
+int uv__dup3(int oldfd, int newfd, int flags) {
+#if !defined(__NR_dup3) || defined(__ANDROID_API__) && __ANDROID_API__ < 21
+  return errno = ENOSYS, -1;
+#else
+  return syscall(__NR_dup3, oldfd, newfd, flags);
+#endif
+}
+
+
+ssize_t
+uv__fs_copy_file_range(int fd_in,
+                       off_t* off_in,
+                       int fd_out,
+                       off_t* off_out,
+                       size_t len,
+                       unsigned int flags)
+{
+#ifdef __NR_copy_file_range
+  return syscall(__NR_copy_file_range,
+                 fd_in,
+                 off_in,
+                 fd_out,
+                 off_out,
+                 len,
+                 flags);
+#else
+  return errno = ENOSYS, -1;
+#endif
+}
+
+
+int uv__statx(int dirfd,
+              const char* path,
+              int flags,
+              unsigned int mask,
+              struct uv__statx* statxbuf) {
+#if !defined(__NR_statx) || defined(__ANDROID_API__) && __ANDROID_API__ < 30
+  return errno = ENOSYS, -1;
+#else
+  return syscall(__NR_statx, dirfd, path, flags, mask, statxbuf);
+#endif
+}
+
+
+ssize_t uv__getrandom(void* buf, size_t buflen, unsigned flags) {
+#if !defined(__NR_getrandom) || defined(__ANDROID_API__) && __ANDROID_API__ < 28
+  return errno = ENOSYS, -1;
+#else
+  return syscall(__NR_getrandom, buf, buflen, flags);
+#endif
+}
diff --git a/wpinet/src/main/native/thirdparty/libuv/src/unix/linux-syscalls.h b/wpinet/src/main/native/thirdparty/libuv/src/unix/linux-syscalls.h
new file mode 100644
index 0000000..b4d9082
--- /dev/null
+++ b/wpinet/src/main/native/thirdparty/libuv/src/unix/linux-syscalls.h
@@ -0,0 +1,78 @@
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#ifndef UV_LINUX_SYSCALL_H_
+#define UV_LINUX_SYSCALL_H_
+
+#include <stdint.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+
+struct uv__statx_timestamp {
+  int64_t tv_sec;
+  uint32_t tv_nsec;
+  int32_t unused0;
+};
+
+struct uv__statx {
+  uint32_t stx_mask;
+  uint32_t stx_blksize;
+  uint64_t stx_attributes;
+  uint32_t stx_nlink;
+  uint32_t stx_uid;
+  uint32_t stx_gid;
+  uint16_t stx_mode;
+  uint16_t unused0;
+  uint64_t stx_ino;
+  uint64_t stx_size;
+  uint64_t stx_blocks;
+  uint64_t stx_attributes_mask;
+  struct uv__statx_timestamp stx_atime;
+  struct uv__statx_timestamp stx_btime;
+  struct uv__statx_timestamp stx_ctime;
+  struct uv__statx_timestamp stx_mtime;
+  uint32_t stx_rdev_major;
+  uint32_t stx_rdev_minor;
+  uint32_t stx_dev_major;
+  uint32_t stx_dev_minor;
+  uint64_t unused1[14];
+};
+
+ssize_t uv__preadv(int fd, const struct iovec *iov, int iovcnt, int64_t offset);
+ssize_t uv__pwritev(int fd, const struct iovec *iov, int iovcnt, int64_t offset);
+int uv__dup3(int oldfd, int newfd, int flags);
+ssize_t
+uv__fs_copy_file_range(int fd_in,
+                       off_t* off_in,
+                       int fd_out,
+                       off_t* off_out,
+                       size_t len,
+                       unsigned int flags);
+int uv__statx(int dirfd,
+              const char* path,
+              int flags,
+              unsigned int mask,
+              struct uv__statx* statxbuf);
+ssize_t uv__getrandom(void* buf, size_t buflen, unsigned flags);
+
+#endif /* UV_LINUX_SYSCALL_H_ */
diff --git a/wpinet/src/main/native/thirdparty/libuv/src/unix/loop-watcher.cpp b/wpinet/src/main/native/thirdparty/libuv/src/unix/loop-watcher.cpp
new file mode 100644
index 0000000..b8c1c2a
--- /dev/null
+++ b/wpinet/src/main/native/thirdparty/libuv/src/unix/loop-watcher.cpp
@@ -0,0 +1,68 @@
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include "uv.h"
+#include "internal.h"
+
+#define UV_LOOP_WATCHER_DEFINE(name, type)                                    \
+  int uv_##name##_init(uv_loop_t* loop, uv_##name##_t* handle) {              \
+    uv__handle_init(loop, (uv_handle_t*)handle, UV_##type);                   \
+    handle->name##_cb = NULL;                                                 \
+    return 0;                                                                 \
+  }                                                                           \
+                                                                              \
+  int uv_##name##_start(uv_##name##_t* handle, uv_##name##_cb cb) {           \
+    if (uv__is_active(handle)) return 0;                                      \
+    if (cb == NULL) return UV_EINVAL;                                         \
+    QUEUE_INSERT_HEAD(&handle->loop->name##_handles, &handle->queue);         \
+    handle->name##_cb = cb;                                                   \
+    uv__handle_start(handle);                                                 \
+    return 0;                                                                 \
+  }                                                                           \
+                                                                              \
+  int uv_##name##_stop(uv_##name##_t* handle) {                               \
+    if (!uv__is_active(handle)) return 0;                                     \
+    QUEUE_REMOVE(&handle->queue);                                             \
+    uv__handle_stop(handle);                                                  \
+    return 0;                                                                 \
+  }                                                                           \
+                                                                              \
+  void uv__run_##name(uv_loop_t* loop) {                                      \
+    uv_##name##_t* h;                                                         \
+    QUEUE queue;                                                              \
+    QUEUE* q;                                                                 \
+    QUEUE_MOVE(&loop->name##_handles, &queue);                                \
+    while (!QUEUE_EMPTY(&queue)) {                                            \
+      q = QUEUE_HEAD(&queue);                                                 \
+      h = QUEUE_DATA(q, uv_##name##_t, queue);                                \
+      QUEUE_REMOVE(q);                                                        \
+      QUEUE_INSERT_TAIL(&loop->name##_handles, q);                            \
+      h->name##_cb(h);                                                        \
+    }                                                                         \
+  }                                                                           \
+                                                                              \
+  void uv__##name##_close(uv_##name##_t* handle) {                            \
+    uv_##name##_stop(handle);                                                 \
+  }
+
+UV_LOOP_WATCHER_DEFINE(prepare, PREPARE)
+UV_LOOP_WATCHER_DEFINE(check, CHECK)
+UV_LOOP_WATCHER_DEFINE(idle, IDLE)
diff --git a/wpinet/src/main/native/thirdparty/libuv/src/unix/loop.cpp b/wpinet/src/main/native/thirdparty/libuv/src/unix/loop.cpp
new file mode 100644
index 0000000..2e819cd
--- /dev/null
+++ b/wpinet/src/main/native/thirdparty/libuv/src/unix/loop.cpp
@@ -0,0 +1,228 @@
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include "uv.h"
+#include "uv/tree.h"
+#include "internal.h"
+#include "heap-inl.h"
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+int uv_loop_init(uv_loop_t* loop) {
+  uv__loop_internal_fields_t* lfields;
+  void* saved_data;
+  int err;
+
+
+  saved_data = loop->data;
+  memset(loop, 0, sizeof(*loop));
+  loop->data = saved_data;
+
+  lfields = (uv__loop_internal_fields_t*) uv__calloc(1, sizeof(*lfields));
+  if (lfields == NULL)
+    return UV_ENOMEM;
+  loop->internal_fields = lfields;
+
+  err = uv_mutex_init(&lfields->loop_metrics.lock);
+  if (err)
+    goto fail_metrics_mutex_init;
+
+  heap_init((struct heap*) &loop->timer_heap);
+  QUEUE_INIT(&loop->wq);
+  QUEUE_INIT(&loop->idle_handles);
+  QUEUE_INIT(&loop->async_handles);
+  QUEUE_INIT(&loop->check_handles);
+  QUEUE_INIT(&loop->prepare_handles);
+  QUEUE_INIT(&loop->handle_queue);
+
+  loop->active_handles = 0;
+  loop->active_reqs.count = 0;
+  loop->nfds = 0;
+  loop->watchers = NULL;
+  loop->nwatchers = 0;
+  QUEUE_INIT(&loop->pending_queue);
+  QUEUE_INIT(&loop->watcher_queue);
+
+  loop->closing_handles = NULL;
+  uv__update_time(loop);
+  loop->async_io_watcher.fd = -1;
+  loop->async_wfd = -1;
+  loop->signal_pipefd[0] = -1;
+  loop->signal_pipefd[1] = -1;
+  loop->backend_fd = -1;
+  loop->emfile_fd = -1;
+
+  loop->timer_counter = 0;
+  loop->stop_flag = 0;
+
+  err = uv__platform_loop_init(loop);
+  if (err)
+    goto fail_platform_init;
+
+  uv__signal_global_once_init();
+  err = uv_signal_init(loop, &loop->child_watcher);
+  if (err)
+    goto fail_signal_init;
+
+  uv__handle_unref(&loop->child_watcher);
+  loop->child_watcher.flags |= UV_HANDLE_INTERNAL;
+  QUEUE_INIT(&loop->process_handles);
+
+  err = uv_rwlock_init(&loop->cloexec_lock);
+  if (err)
+    goto fail_rwlock_init;
+
+  err = uv_mutex_init(&loop->wq_mutex);
+  if (err)
+    goto fail_mutex_init;
+
+  err = uv_async_init(loop, &loop->wq_async, uv__work_done);
+  if (err)
+    goto fail_async_init;
+
+  uv__handle_unref(&loop->wq_async);
+  loop->wq_async.flags |= UV_HANDLE_INTERNAL;
+
+  return 0;
+
+fail_async_init:
+  uv_mutex_destroy(&loop->wq_mutex);
+
+fail_mutex_init:
+  uv_rwlock_destroy(&loop->cloexec_lock);
+
+fail_rwlock_init:
+  uv__signal_loop_cleanup(loop);
+
+fail_signal_init:
+  uv__platform_loop_delete(loop);
+
+fail_platform_init:
+  uv_mutex_destroy(&lfields->loop_metrics.lock);
+
+fail_metrics_mutex_init:
+  uv__free(lfields);
+  loop->internal_fields = NULL;
+
+  uv__free(loop->watchers);
+  loop->nwatchers = 0;
+  return err;
+}
+
+
+int uv_loop_fork(uv_loop_t* loop) {
+  int err;
+  unsigned int i;
+  uv__io_t* w;
+
+  err = uv__io_fork(loop);
+  if (err)
+    return err;
+
+  err = uv__async_fork(loop);
+  if (err)
+    return err;
+
+  err = uv__signal_loop_fork(loop);
+  if (err)
+    return err;
+
+  /* Rearm all the watchers that aren't re-queued by the above. */
+  for (i = 0; i < loop->nwatchers; i++) {
+    w = (uv__io_t*)loop->watchers[i];
+    if (w == NULL)
+      continue;
+
+    if (w->pevents != 0 && QUEUE_EMPTY(&w->watcher_queue)) {
+      w->events = 0; /* Force re-registration in uv__io_poll. */
+      QUEUE_INSERT_TAIL(&loop->watcher_queue, &w->watcher_queue);
+    }
+  }
+
+  return 0;
+}
+
+
+void uv__loop_close(uv_loop_t* loop) {
+  uv__loop_internal_fields_t* lfields;
+
+  uv__signal_loop_cleanup(loop);
+  uv__platform_loop_delete(loop);
+  uv__async_stop(loop);
+
+  if (loop->emfile_fd != -1) {
+    uv__close(loop->emfile_fd);
+    loop->emfile_fd = -1;
+  }
+
+  if (loop->backend_fd != -1) {
+    uv__close(loop->backend_fd);
+    loop->backend_fd = -1;
+  }
+
+  uv_mutex_lock(&loop->wq_mutex);
+  assert(QUEUE_EMPTY(&loop->wq) && "thread pool work queue not empty!");
+  assert(!uv__has_active_reqs(loop));
+  uv_mutex_unlock(&loop->wq_mutex);
+  uv_mutex_destroy(&loop->wq_mutex);
+
+  /*
+   * Note that all thread pool stuff is finished at this point and
+   * it is safe to just destroy rw lock
+   */
+  uv_rwlock_destroy(&loop->cloexec_lock);
+
+#if 0
+  assert(QUEUE_EMPTY(&loop->pending_queue));
+  assert(QUEUE_EMPTY(&loop->watcher_queue));
+  assert(loop->nfds == 0);
+#endif
+
+  uv__free(loop->watchers);
+  loop->watchers = NULL;
+  loop->nwatchers = 0;
+
+  lfields = uv__get_internal_fields(loop);
+  uv_mutex_destroy(&lfields->loop_metrics.lock);
+  uv__free(lfields);
+  loop->internal_fields = NULL;
+}
+
+
+int uv__loop_configure(uv_loop_t* loop, uv_loop_option option, va_list ap) {
+  uv__loop_internal_fields_t* lfields;
+
+  lfields = uv__get_internal_fields(loop);
+  if (option == UV_METRICS_IDLE_TIME) {
+    lfields->flags |= UV_METRICS_IDLE_TIME;
+    return 0;
+  }
+
+  if (option != UV_LOOP_BLOCK_SIGNAL)
+    return UV_ENOSYS;
+
+  if (va_arg(ap, int) != SIGPROF)
+    return UV_EINVAL;
+
+  loop->flags |= UV_LOOP_BLOCK_SIGPROF;
+  return 0;
+}
diff --git a/wpinet/src/main/native/thirdparty/libuv/src/unix/netbsd.cpp b/wpinet/src/main/native/thirdparty/libuv/src/unix/netbsd.cpp
new file mode 100644
index 0000000..b6886a1
--- /dev/null
+++ b/wpinet/src/main/native/thirdparty/libuv/src/unix/netbsd.cpp
@@ -0,0 +1,259 @@
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include "uv.h"
+#include "internal.h"
+
+#include <assert.h>
+#include <string.h>
+#include <errno.h>
+
+#include <kvm.h>
+#include <paths.h>
+#include <unistd.h>
+#include <time.h>
+#include <stdlib.h>
+#include <fcntl.h>
+
+#include <sys/resource.h>
+#include <sys/types.h>
+#include <sys/sysctl.h>
+#include <uvm/uvm_extern.h>
+
+#include <unistd.h>
+#include <time.h>
+
+
+int uv__platform_loop_init(uv_loop_t* loop) {
+  return uv__kqueue_init(loop);
+}
+
+
+void uv__platform_loop_delete(uv_loop_t* loop) {
+}
+
+
+void uv_loadavg(double avg[3]) {
+  struct loadavg info;
+  size_t size = sizeof(info);
+  int which[] = {CTL_VM, VM_LOADAVG};
+
+  if (sysctl(which, ARRAY_SIZE(which), &info, &size, NULL, 0) == -1) return;
+
+  avg[0] = (double) info.ldavg[0] / info.fscale;
+  avg[1] = (double) info.ldavg[1] / info.fscale;
+  avg[2] = (double) info.ldavg[2] / info.fscale;
+}
+
+
+int uv_exepath(char* buffer, size_t* size) {
+  /* Intermediate buffer, retrieving partial path name does not work
+   * As of NetBSD-8(beta), vnode->path translator does not handle files
+   * with longer names than 31 characters.
+   */
+  char int_buf[PATH_MAX];
+  size_t int_size;
+  int mib[4];
+
+  if (buffer == NULL || size == NULL || *size == 0)
+    return UV_EINVAL;
+
+  mib[0] = CTL_KERN;
+  mib[1] = KERN_PROC_ARGS;
+  mib[2] = -1;
+  mib[3] = KERN_PROC_PATHNAME;
+  int_size = ARRAY_SIZE(int_buf);
+
+  if (sysctl(mib, 4, int_buf, &int_size, NULL, 0))
+    return UV__ERR(errno);
+
+  /* Copy string from the intermediate buffer to outer one with appropriate
+   * length.
+   */
+  /* TODO(bnoordhuis) Check uv__strscpy() return value. */
+  uv__strscpy(buffer, int_buf, *size);
+
+  /* Set new size. */
+  *size = strlen(buffer);
+
+  return 0;
+}
+
+
+uint64_t uv_get_free_memory(void) {
+  struct uvmexp info;
+  size_t size = sizeof(info);
+  int which[] = {CTL_VM, VM_UVMEXP};
+
+  if (sysctl(which, ARRAY_SIZE(which), &info, &size, NULL, 0))
+    return UV__ERR(errno);
+
+  return (uint64_t) info.free * sysconf(_SC_PAGESIZE);
+}
+
+
+uint64_t uv_get_total_memory(void) {
+#if defined(HW_PHYSMEM64)
+  uint64_t info;
+  int which[] = {CTL_HW, HW_PHYSMEM64};
+#else
+  unsigned int info;
+  int which[] = {CTL_HW, HW_PHYSMEM};
+#endif
+  size_t size = sizeof(info);
+
+  if (sysctl(which, ARRAY_SIZE(which), &info, &size, NULL, 0))
+    return UV__ERR(errno);
+
+  return (uint64_t) info;
+}
+
+
+uint64_t uv_get_constrained_memory(void) {
+  return 0;  /* Memory constraints are unknown. */
+}
+
+
+int uv_resident_set_memory(size_t* rss) {
+  kvm_t *kd = NULL;
+  struct kinfo_proc2 *kinfo = NULL;
+  pid_t pid;
+  int nprocs;
+  int max_size = sizeof(struct kinfo_proc2);
+  int page_size;
+
+  page_size = getpagesize();
+  pid = getpid();
+
+  kd = kvm_open(NULL, NULL, NULL, KVM_NO_FILES, "kvm_open");
+
+  if (kd == NULL) goto error;
+
+  kinfo = kvm_getproc2(kd, KERN_PROC_PID, pid, max_size, &nprocs);
+  if (kinfo == NULL) goto error;
+
+  *rss = kinfo->p_vm_rssize * page_size;
+
+  kvm_close(kd);
+
+  return 0;
+
+error:
+  if (kd) kvm_close(kd);
+  return UV_EPERM;
+}
+
+
+int uv_uptime(double* uptime) {
+  time_t now;
+  struct timeval info;
+  size_t size = sizeof(info);
+  static int which[] = {CTL_KERN, KERN_BOOTTIME};
+
+  if (sysctl(which, ARRAY_SIZE(which), &info, &size, NULL, 0))
+    return UV__ERR(errno);
+
+  now = time(NULL);
+
+  *uptime = (double)(now - info.tv_sec);
+  return 0;
+}
+
+
+int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) {
+  unsigned int ticks = (unsigned int)sysconf(_SC_CLK_TCK);
+  unsigned int multiplier = ((uint64_t)1000L / ticks);
+  unsigned int cur = 0;
+  uv_cpu_info_t* cpu_info;
+  u_int64_t* cp_times;
+  char model[512];
+  u_int64_t cpuspeed;
+  int numcpus;
+  size_t size;
+  int i;
+
+  size = sizeof(model);
+  if (sysctlbyname("machdep.cpu_brand", &model, &size, NULL, 0) &&
+      sysctlbyname("hw.model", &model, &size, NULL, 0)) {
+    return UV__ERR(errno);
+  }
+
+  size = sizeof(numcpus);
+  if (sysctlbyname("hw.ncpu", &numcpus, &size, NULL, 0))
+    return UV__ERR(errno);
+  *count = numcpus;
+
+  /* Only i386 and amd64 have machdep.tsc_freq */
+  size = sizeof(cpuspeed);
+  if (sysctlbyname("machdep.tsc_freq", &cpuspeed, &size, NULL, 0))
+    cpuspeed = 0;
+
+  size = numcpus * CPUSTATES * sizeof(*cp_times);
+  cp_times = (u_int64_t*)uv__malloc(size);
+  if (cp_times == NULL)
+    return UV_ENOMEM;
+
+  if (sysctlbyname("kern.cp_time", cp_times, &size, NULL, 0))
+    return UV__ERR(errno);
+
+  *cpu_infos = (uv_cpu_info_t*)uv__malloc(numcpus * sizeof(**cpu_infos));
+  if (!(*cpu_infos)) {
+    uv__free(cp_times);
+    uv__free(*cpu_infos);
+    return UV_ENOMEM;
+  }
+
+  for (i = 0; i < numcpus; i++) {
+    cpu_info = &(*cpu_infos)[i];
+    cpu_info->cpu_times.user = (uint64_t)(cp_times[CP_USER+cur]) * multiplier;
+    cpu_info->cpu_times.nice = (uint64_t)(cp_times[CP_NICE+cur]) * multiplier;
+    cpu_info->cpu_times.sys = (uint64_t)(cp_times[CP_SYS+cur]) * multiplier;
+    cpu_info->cpu_times.idle = (uint64_t)(cp_times[CP_IDLE+cur]) * multiplier;
+    cpu_info->cpu_times.irq = (uint64_t)(cp_times[CP_INTR+cur]) * multiplier;
+    cpu_info->model = uv__strdup(model);
+    cpu_info->speed = (int)(cpuspeed/(uint64_t) 1e6);
+    cur += CPUSTATES;
+  }
+  uv__free(cp_times);
+  return 0;
+}
+
+int uv__random_sysctl(void* buf, size_t len) {
+  static int name[] = {CTL_KERN, KERN_ARND};
+  size_t count, req;
+  unsigned char* p;
+
+  p = buf;
+  while (len) {
+    req = len < 32 ? len : 32;
+    count = req;
+
+    if (sysctl(name, ARRAY_SIZE(name), p, &count, NULL, 0) == -1)
+      return UV__ERR(errno);
+
+    if (count != req)
+      return UV_EIO;  /* Can't happen. */
+
+    p += count;
+    len -= count;
+  }
+
+  return 0;
+}
diff --git a/wpinet/src/main/native/thirdparty/libuv/src/unix/no-fsevents.cpp b/wpinet/src/main/native/thirdparty/libuv/src/unix/no-fsevents.cpp
new file mode 100644
index 0000000..158643a
--- /dev/null
+++ b/wpinet/src/main/native/thirdparty/libuv/src/unix/no-fsevents.cpp
@@ -0,0 +1,42 @@
+/* Copyright libuv project contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include "uv.h"
+#include "internal.h"
+
+#include <errno.h>
+
+int uv_fs_event_init(uv_loop_t* loop, uv_fs_event_t* handle) {
+  return UV_ENOSYS;
+}
+
+int uv_fs_event_start(uv_fs_event_t* handle, uv_fs_event_cb cb,
+                      const char* filename, unsigned int flags) {
+  return UV_ENOSYS;
+}
+
+int uv_fs_event_stop(uv_fs_event_t* handle) {
+  return UV_ENOSYS;
+}
+
+void uv__fs_event_close(uv_fs_event_t* handle) {
+  UNREACHABLE();
+}
diff --git a/wpinet/src/main/native/thirdparty/libuv/src/unix/no-proctitle.cpp b/wpinet/src/main/native/thirdparty/libuv/src/unix/no-proctitle.cpp
new file mode 100644
index 0000000..32aa0af
--- /dev/null
+++ b/wpinet/src/main/native/thirdparty/libuv/src/unix/no-proctitle.cpp
@@ -0,0 +1,45 @@
+/* Copyright libuv project contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include "uv.h"
+#include "internal.h"
+
+#include <errno.h>
+#include <stddef.h>
+
+char** uv_setup_args(int argc, char** argv) {
+  return argv;
+}
+
+void uv__process_title_cleanup(void) {
+}
+
+int uv_set_process_title(const char* title) {
+  return 0;
+}
+
+int uv_get_process_title(char* buffer, size_t size) {
+  if (buffer == NULL || size == 0)
+    return UV_EINVAL;
+
+  buffer[0] = '\0';
+  return 0;
+}
diff --git a/wpinet/src/main/native/thirdparty/libuv/src/unix/openbsd.cpp b/wpinet/src/main/native/thirdparty/libuv/src/unix/openbsd.cpp
new file mode 100644
index 0000000..62740f7
--- /dev/null
+++ b/wpinet/src/main/native/thirdparty/libuv/src/unix/openbsd.cpp
@@ -0,0 +1,240 @@
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include "uv.h"
+#include "internal.h"
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/resource.h>
+#include <sys/sched.h>
+#include <sys/time.h>
+#include <sys/sysctl.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <paths.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+
+int uv__platform_loop_init(uv_loop_t* loop) {
+  return uv__kqueue_init(loop);
+}
+
+
+void uv__platform_loop_delete(uv_loop_t* loop) {
+}
+
+
+void uv_loadavg(double avg[3]) {
+  struct loadavg info;
+  size_t size = sizeof(info);
+  int which[] = {CTL_VM, VM_LOADAVG};
+
+  if (sysctl(which, ARRAY_SIZE(which), &info, &size, NULL, 0) < 0) return;
+
+  avg[0] = (double) info.ldavg[0] / info.fscale;
+  avg[1] = (double) info.ldavg[1] / info.fscale;
+  avg[2] = (double) info.ldavg[2] / info.fscale;
+}
+
+
+int uv_exepath(char* buffer, size_t* size) {
+  int mib[4];
+  char **argsbuf = NULL;
+  size_t argsbuf_size = 100U;
+  size_t exepath_size;
+  pid_t mypid;
+  int err;
+
+  if (buffer == NULL || size == NULL || *size == 0)
+    return UV_EINVAL;
+
+  mypid = getpid();
+  for (;;) {
+    err = UV_ENOMEM;
+    argsbuf = (char**)uv__reallocf(argsbuf, argsbuf_size);
+    if (argsbuf == NULL)
+      goto out;
+    mib[0] = CTL_KERN;
+    mib[1] = KERN_PROC_ARGS;
+    mib[2] = mypid;
+    mib[3] = KERN_PROC_ARGV;
+    if (sysctl(mib, ARRAY_SIZE(mib), argsbuf, &argsbuf_size, NULL, 0) == 0) {
+      break;
+    }
+    if (errno != ENOMEM) {
+      err = UV__ERR(errno);
+      goto out;
+    }
+    argsbuf_size *= 2U;
+  }
+
+  if (argsbuf[0] == NULL) {
+    err = UV_EINVAL;  /* FIXME(bnoordhuis) More appropriate error. */
+    goto out;
+  }
+
+  *size -= 1;
+  exepath_size = strlen(argsbuf[0]);
+  if (*size > exepath_size)
+    *size = exepath_size;
+
+  memcpy(buffer, argsbuf[0], *size);
+  buffer[*size] = '\0';
+  err = 0;
+
+out:
+  uv__free(argsbuf);
+
+  return err;
+}
+
+
+uint64_t uv_get_free_memory(void) {
+  struct uvmexp info;
+  size_t size = sizeof(info);
+  int which[] = {CTL_VM, VM_UVMEXP};
+
+  if (sysctl(which, ARRAY_SIZE(which), &info, &size, NULL, 0))
+    return UV__ERR(errno);
+
+  return (uint64_t) info.free * sysconf(_SC_PAGESIZE);
+}
+
+
+uint64_t uv_get_total_memory(void) {
+  uint64_t info;
+  int which[] = {CTL_HW, HW_PHYSMEM64};
+  size_t size = sizeof(info);
+
+  if (sysctl(which, ARRAY_SIZE(which), &info, &size, NULL, 0))
+    return UV__ERR(errno);
+
+  return (uint64_t) info;
+}
+
+
+uint64_t uv_get_constrained_memory(void) {
+  return 0;  /* Memory constraints are unknown. */
+}
+
+
+int uv_resident_set_memory(size_t* rss) {
+  struct kinfo_proc kinfo;
+  size_t page_size = getpagesize();
+  size_t size = sizeof(struct kinfo_proc);
+  int mib[6];
+
+  mib[0] = CTL_KERN;
+  mib[1] = KERN_PROC;
+  mib[2] = KERN_PROC_PID;
+  mib[3] = getpid();
+  mib[4] = sizeof(struct kinfo_proc);
+  mib[5] = 1;
+
+  if (sysctl(mib, ARRAY_SIZE(mib), &kinfo, &size, NULL, 0) < 0)
+    return UV__ERR(errno);
+
+  *rss = kinfo.p_vm_rssize * page_size;
+  return 0;
+}
+
+
+int uv_uptime(double* uptime) {
+  time_t now;
+  struct timeval info;
+  size_t size = sizeof(info);
+  static int which[] = {CTL_KERN, KERN_BOOTTIME};
+
+  if (sysctl(which, ARRAY_SIZE(which), &info, &size, NULL, 0))
+    return UV__ERR(errno);
+
+  now = time(NULL);
+
+  *uptime = (double)(now - info.tv_sec);
+  return 0;
+}
+
+
+int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) {
+  unsigned int ticks = (unsigned int)sysconf(_SC_CLK_TCK),
+               multiplier = ((uint64_t)1000L / ticks), cpuspeed;
+  uint64_t info[CPUSTATES];
+  char model[512];
+  int numcpus = 1;
+  int which[] = {CTL_HW,HW_MODEL};
+  int percpu[] = {CTL_KERN,KERN_CPTIME2,0};
+  size_t size;
+  int i, j;
+  uv_cpu_info_t* cpu_info;
+
+  size = sizeof(model);
+  if (sysctl(which, ARRAY_SIZE(which), &model, &size, NULL, 0))
+    return UV__ERR(errno);
+
+  which[1] = HW_NCPUONLINE;
+  size = sizeof(numcpus);
+  if (sysctl(which, ARRAY_SIZE(which), &numcpus, &size, NULL, 0))
+    return UV__ERR(errno);
+
+  *cpu_infos = (uv_cpu_info_t*)uv__malloc(numcpus * sizeof(**cpu_infos));
+  if (!(*cpu_infos))
+    return UV_ENOMEM;
+
+  i = 0;
+  *count = numcpus;
+
+  which[1] = HW_CPUSPEED;
+  size = sizeof(cpuspeed);
+  if (sysctl(which, ARRAY_SIZE(which), &cpuspeed, &size, NULL, 0))
+    goto error;
+
+  size = sizeof(info);
+  for (i = 0; i < numcpus; i++) {
+    percpu[2] = i;
+    if (sysctl(percpu, ARRAY_SIZE(percpu), &info, &size, NULL, 0))
+      goto error;
+
+    cpu_info = &(*cpu_infos)[i];
+
+    cpu_info->cpu_times.user = (uint64_t)(info[CP_USER]) * multiplier;
+    cpu_info->cpu_times.nice = (uint64_t)(info[CP_NICE]) * multiplier;
+    cpu_info->cpu_times.sys = (uint64_t)(info[CP_SYS]) * multiplier;
+    cpu_info->cpu_times.idle = (uint64_t)(info[CP_IDLE]) * multiplier;
+    cpu_info->cpu_times.irq = (uint64_t)(info[CP_INTR]) * multiplier;
+
+    cpu_info->model = uv__strdup(model);
+    cpu_info->speed = cpuspeed;
+  }
+
+  return 0;
+
+error:
+  *count = 0;
+  for (j = 0; j < i; j++)
+    uv__free((*cpu_infos)[j].model);
+
+  uv__free(*cpu_infos);
+  *cpu_infos = NULL;
+  return UV__ERR(errno);
+}
diff --git a/wpinet/src/main/native/thirdparty/libuv/src/unix/pipe.cpp b/wpinet/src/main/native/thirdparty/libuv/src/unix/pipe.cpp
new file mode 100644
index 0000000..c8ba31d
--- /dev/null
+++ b/wpinet/src/main/native/thirdparty/libuv/src/unix/pipe.cpp
@@ -0,0 +1,437 @@
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include "uv.h"
+#include "internal.h"
+
+#include <assert.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/un.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+
+int uv_pipe_init(uv_loop_t* loop, uv_pipe_t* handle, int ipc) {
+  uv__stream_init(loop, (uv_stream_t*)handle, UV_NAMED_PIPE);
+  handle->shutdown_req = NULL;
+  handle->connect_req = NULL;
+  handle->pipe_fname = NULL;
+  handle->ipc = ipc;
+  return 0;
+}
+
+
+int uv_pipe_bind(uv_pipe_t* handle, const char* name) {
+  struct sockaddr_un saddr;
+  const char* pipe_fname;
+  int sockfd;
+  int err;
+
+  pipe_fname = NULL;
+
+  /* Already bound? */
+  if (uv__stream_fd(handle) >= 0)
+    return UV_EINVAL;
+  if (uv__is_closing(handle)) {
+    return UV_EINVAL;
+  }
+  /* Make a copy of the file name, it outlives this function's scope. */
+  pipe_fname = uv__strdup(name);
+  if (pipe_fname == NULL)
+    return UV_ENOMEM;
+
+  /* We've got a copy, don't touch the original any more. */
+  name = NULL;
+
+  err = uv__socket(AF_UNIX, SOCK_STREAM, 0);
+  if (err < 0)
+    goto err_socket;
+  sockfd = err;
+
+  memset(&saddr, 0, sizeof saddr);
+  uv__strscpy(saddr.sun_path, pipe_fname, sizeof(saddr.sun_path));
+  saddr.sun_family = AF_UNIX;
+
+  if (bind(sockfd, (struct sockaddr*)&saddr, sizeof saddr)) {
+    err = UV__ERR(errno);
+    /* Convert ENOENT to EACCES for compatibility with Windows. */
+    if (err == UV_ENOENT)
+      err = UV_EACCES;
+
+    uv__close(sockfd);
+    goto err_socket;
+  }
+
+  /* Success. */
+  handle->flags |= UV_HANDLE_BOUND;
+  handle->pipe_fname = pipe_fname; /* Is a strdup'ed copy. */
+  handle->io_watcher.fd = sockfd;
+  return 0;
+
+err_socket:
+  uv__free((void*)pipe_fname);
+  return err;
+}
+
+
+int uv__pipe_listen(uv_pipe_t* handle, int backlog, uv_connection_cb cb) {
+  if (uv__stream_fd(handle) == -1)
+    return UV_EINVAL;
+
+  if (handle->ipc)
+    return UV_EINVAL;
+
+#if defined(__MVS__) || defined(__PASE__)
+  /* On zOS, backlog=0 has undefined behaviour */
+  /* On IBMi PASE, backlog=0 leads to "Connection refused" error */
+  if (backlog == 0)
+    backlog = 1;
+  else if (backlog < 0)
+    backlog = SOMAXCONN;
+#endif
+
+  if (listen(uv__stream_fd(handle), backlog))
+    return UV__ERR(errno);
+
+  handle->connection_cb = cb;
+  handle->io_watcher.cb = uv__server_io;
+  uv__io_start(handle->loop, &handle->io_watcher, POLLIN);
+  return 0;
+}
+
+
+void uv__pipe_close(uv_pipe_t* handle) {
+  if (handle->pipe_fname) {
+    /*
+     * Unlink the file system entity before closing the file descriptor.
+     * Doing it the other way around introduces a race where our process
+     * unlinks a socket with the same name that's just been created by
+     * another thread or process.
+     */
+    unlink(handle->pipe_fname);
+    uv__free((void*)handle->pipe_fname);
+    handle->pipe_fname = NULL;
+  }
+
+  uv__stream_close((uv_stream_t*)handle);
+}
+
+
+int uv_pipe_open(uv_pipe_t* handle, uv_file fd) {
+  int flags;
+  int mode;
+  int err;
+  flags = 0;
+
+  if (uv__fd_exists(handle->loop, fd))
+    return UV_EEXIST;
+
+  do
+    mode = fcntl(fd, F_GETFL);
+  while (mode == -1 && errno == EINTR);
+
+  if (mode == -1)
+    return UV__ERR(errno); /* according to docs, must be EBADF */
+
+  err = uv__nonblock(fd, 1);
+  if (err)
+    return err;
+
+#if defined(__APPLE__)
+  err = uv__stream_try_select((uv_stream_t*) handle, &fd);
+  if (err)
+    return err;
+#endif /* defined(__APPLE__) */
+
+  mode &= O_ACCMODE;
+  if (mode != O_WRONLY)
+    flags |= UV_HANDLE_READABLE;
+  if (mode != O_RDONLY)
+    flags |= UV_HANDLE_WRITABLE;
+
+  return uv__stream_open((uv_stream_t*)handle, fd, flags);
+}
+
+
+void uv_pipe_connect(uv_connect_t* req,
+                    uv_pipe_t* handle,
+                    const char* name,
+                    uv_connect_cb cb) {
+  struct sockaddr_un saddr;
+  int new_sock;
+  int err;
+  int r;
+
+  new_sock = (uv__stream_fd(handle) == -1);
+
+  if (new_sock) {
+    err = uv__socket(AF_UNIX, SOCK_STREAM, 0);
+    if (err < 0)
+      goto out;
+    handle->io_watcher.fd = err;
+  }
+
+  memset(&saddr, 0, sizeof saddr);
+  uv__strscpy(saddr.sun_path, name, sizeof(saddr.sun_path));
+  saddr.sun_family = AF_UNIX;
+
+  do {
+    r = connect(uv__stream_fd(handle),
+                (struct sockaddr*)&saddr, sizeof saddr);
+  }
+  while (r == -1 && errno == EINTR);
+
+  if (r == -1 && errno != EINPROGRESS) {
+    err = UV__ERR(errno);
+#if defined(__CYGWIN__) || defined(__MSYS__)
+    /* EBADF is supposed to mean that the socket fd is bad, but
+       Cygwin reports EBADF instead of ENOTSOCK when the file is
+       not a socket.  We do not expect to see a bad fd here
+       (e.g. due to new_sock), so translate the error.  */
+    if (err == UV_EBADF)
+      err = UV_ENOTSOCK;
+#endif
+    goto out;
+  }
+
+  err = 0;
+  if (new_sock) {
+    err = uv__stream_open((uv_stream_t*)handle,
+                          uv__stream_fd(handle),
+                          UV_HANDLE_READABLE | UV_HANDLE_WRITABLE);
+  }
+
+  if (err == 0)
+    uv__io_start(handle->loop, &handle->io_watcher, POLLOUT);
+
+out:
+  handle->delayed_error = err;
+  handle->connect_req = req;
+
+  uv__req_init(handle->loop, req, UV_CONNECT);
+  req->handle = (uv_stream_t*)handle;
+  req->cb = cb;
+  QUEUE_INIT(&req->queue);
+
+  /* Force callback to run on next tick in case of error. */
+  if (err)
+    uv__io_feed(handle->loop, &handle->io_watcher);
+
+}
+
+
+static int uv__pipe_getsockpeername(const uv_pipe_t* handle,
+                                    uv__peersockfunc func,
+                                    char* buffer,
+                                    size_t* size) {
+  struct sockaddr_un sa;
+  socklen_t addrlen;
+  int err;
+
+  addrlen = sizeof(sa);
+  memset(&sa, 0, addrlen);
+  err = uv__getsockpeername((const uv_handle_t*) handle,
+                            func,
+                            (struct sockaddr*) &sa,
+                            (int*) &addrlen);
+  if (err < 0) {
+    *size = 0;
+    return err;
+  }
+
+#if defined(__linux__)
+  if (sa.sun_path[0] == 0)
+    /* Linux abstract namespace */
+    addrlen -= offsetof(struct sockaddr_un, sun_path);
+  else
+#endif
+    addrlen = strlen(sa.sun_path);
+
+
+  if ((size_t)addrlen >= *size) {
+    *size = addrlen + 1;
+    return UV_ENOBUFS;
+  }
+
+  memcpy(buffer, sa.sun_path, addrlen);
+  *size = addrlen;
+
+  /* only null-terminate if it's not an abstract socket */
+  if (buffer[0] != '\0')
+    buffer[addrlen] = '\0';
+
+  return 0;
+}
+
+
+int uv_pipe_getsockname(const uv_pipe_t* handle, char* buffer, size_t* size) {
+  return uv__pipe_getsockpeername(handle, getsockname, buffer, size);
+}
+
+
+int uv_pipe_getpeername(const uv_pipe_t* handle, char* buffer, size_t* size) {
+  return uv__pipe_getsockpeername(handle, getpeername, buffer, size);
+}
+
+
+void uv_pipe_pending_instances(uv_pipe_t* handle, int count) {
+}
+
+
+int uv_pipe_pending_count(uv_pipe_t* handle) {
+  uv__stream_queued_fds_t* queued_fds;
+
+  if (!handle->ipc)
+    return 0;
+
+  if (handle->accepted_fd == -1)
+    return 0;
+
+  if (handle->queued_fds == NULL)
+    return 1;
+
+  queued_fds = (uv__stream_queued_fds_t*)(handle->queued_fds);
+  return queued_fds->offset + 1;
+}
+
+
+uv_handle_type uv_pipe_pending_type(uv_pipe_t* handle) {
+  if (!handle->ipc)
+    return UV_UNKNOWN_HANDLE;
+
+  if (handle->accepted_fd == -1)
+    return UV_UNKNOWN_HANDLE;
+  else
+    return uv_guess_handle(handle->accepted_fd);
+}
+
+
+int uv_pipe_chmod(uv_pipe_t* handle, int mode) {
+  unsigned desired_mode;
+  struct stat pipe_stat;
+  char* name_buffer;
+  size_t name_len;
+  int r;
+
+  if (handle == NULL || uv__stream_fd(handle) == -1)
+    return UV_EBADF;
+
+  if (mode != UV_READABLE &&
+      mode != UV_WRITABLE &&
+      mode != (UV_WRITABLE | UV_READABLE))
+    return UV_EINVAL;
+
+  /* Unfortunately fchmod does not work on all platforms, we will use chmod. */
+  name_len = 0;
+  r = uv_pipe_getsockname(handle, NULL, &name_len);
+  if (r != UV_ENOBUFS)
+    return r;
+
+  name_buffer = (char*)uv__malloc(name_len);
+  if (name_buffer == NULL)
+    return UV_ENOMEM;
+
+  r = uv_pipe_getsockname(handle, name_buffer, &name_len);
+  if (r != 0) {
+    uv__free(name_buffer);
+    return r;
+  }
+
+  /* stat must be used as fstat has a bug on Darwin */
+  if (stat(name_buffer, &pipe_stat) == -1) {
+    uv__free(name_buffer);
+    return -errno;
+  }
+
+  desired_mode = 0;
+  if (mode & UV_READABLE)
+    desired_mode |= S_IRUSR | S_IRGRP | S_IROTH;
+  if (mode & UV_WRITABLE)
+    desired_mode |= S_IWUSR | S_IWGRP | S_IWOTH;
+
+  /* Exit early if pipe already has desired mode. */
+  if ((pipe_stat.st_mode & desired_mode) == desired_mode) {
+    uv__free(name_buffer);
+    return 0;
+  }
+
+  pipe_stat.st_mode |= desired_mode;
+
+  r = chmod(name_buffer, pipe_stat.st_mode);
+  uv__free(name_buffer);
+
+  return r != -1 ? 0 : UV__ERR(errno);
+}
+
+
+int uv_pipe(uv_os_fd_t fds[2], int read_flags, int write_flags) {
+  uv_os_fd_t temp[2];
+  int err;
+#if defined(__FreeBSD__) || defined(__linux__)
+  int flags = O_CLOEXEC;
+
+  if ((read_flags & UV_NONBLOCK_PIPE) && (write_flags & UV_NONBLOCK_PIPE))
+    flags |= UV_FS_O_NONBLOCK;
+
+  if (pipe2(temp, flags))
+    return UV__ERR(errno);
+
+  if (flags & UV_FS_O_NONBLOCK) {
+    fds[0] = temp[0];
+    fds[1] = temp[1];
+    return 0;
+  }
+#else
+  if (pipe(temp))
+    return UV__ERR(errno);
+
+  if ((err = uv__cloexec(temp[0], 1)))
+    goto fail;
+
+  if ((err = uv__cloexec(temp[1], 1)))
+    goto fail;
+#endif
+
+  if (read_flags & UV_NONBLOCK_PIPE)
+    if ((err = uv__nonblock(temp[0], 1)))
+      goto fail;
+
+  if (write_flags & UV_NONBLOCK_PIPE)
+    if ((err = uv__nonblock(temp[1], 1)))
+      goto fail;
+
+  fds[0] = temp[0];
+  fds[1] = temp[1];
+  return 0;
+
+fail:
+  uv__close(temp[0]);
+  uv__close(temp[1]);
+  return err;
+}
+
+
+int uv__make_pipe(int fds[2], int flags) {
+  return uv_pipe(fds,
+                 flags & UV_NONBLOCK_PIPE,
+                 flags & UV_NONBLOCK_PIPE);
+}
diff --git a/wpinet/src/main/native/thirdparty/libuv/src/unix/poll.cpp b/wpinet/src/main/native/thirdparty/libuv/src/unix/poll.cpp
new file mode 100644
index 0000000..7364731
--- /dev/null
+++ b/wpinet/src/main/native/thirdparty/libuv/src/unix/poll.cpp
@@ -0,0 +1,160 @@
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include "uv.h"
+#include "internal.h"
+
+#include <unistd.h>
+#include <assert.h>
+#include <errno.h>
+
+
+static void uv__poll_io(uv_loop_t* loop, uv__io_t* w, unsigned int events) {
+  uv_poll_t* handle;
+  int pevents;
+
+  handle = container_of(w, uv_poll_t, io_watcher);
+
+  /*
+   * As documented in the kernel source fs/kernfs/file.c #780
+   * poll will return POLLERR|POLLPRI in case of sysfs
+   * polling. This does not happen in case of out-of-band
+   * TCP messages.
+   *
+   * The above is the case on (at least) FreeBSD and Linux.
+   *
+   * So to properly determine a POLLPRI or a POLLERR we need
+   * to check for both.
+   */
+  if ((events & POLLERR) && !(events & UV__POLLPRI)) {
+    uv__io_stop(loop, w, POLLIN | POLLOUT | UV__POLLRDHUP | UV__POLLPRI);
+    uv__handle_stop(handle);
+    handle->poll_cb(handle, UV_EBADF, 0);
+    return;
+  }
+
+  pevents = 0;
+  if (events & POLLIN)
+    pevents |= UV_READABLE;
+  if (events & UV__POLLPRI)
+    pevents |= UV_PRIORITIZED;
+  if (events & POLLOUT)
+    pevents |= UV_WRITABLE;
+  if (events & UV__POLLRDHUP)
+    pevents |= UV_DISCONNECT;
+
+  handle->poll_cb(handle, 0, pevents);
+}
+
+
+int uv_poll_init(uv_loop_t* loop, uv_poll_t* handle, int fd) {
+  int err;
+
+  if (uv__fd_exists(loop, fd))
+    return UV_EEXIST;
+
+  err = uv__io_check_fd(loop, fd);
+  if (err)
+    return err;
+
+  /* If ioctl(FIONBIO) reports ENOTTY, try fcntl(F_GETFL) + fcntl(F_SETFL).
+   * Workaround for e.g. kqueue fds not supporting ioctls.
+   */
+  err = uv__nonblock(fd, 1);
+#if UV__NONBLOCK_IS_IOCTL
+  if (err == UV_ENOTTY)
+    err = uv__nonblock_fcntl(fd, 1);
+#endif
+
+  if (err)
+    return err;
+
+  uv__handle_init(loop, (uv_handle_t*) handle, UV_POLL);
+  uv__io_init(&handle->io_watcher, uv__poll_io, fd);
+  handle->poll_cb = NULL;
+  return 0;
+}
+
+
+int uv_poll_init_socket(uv_loop_t* loop, uv_poll_t* handle,
+    uv_os_sock_t socket) {
+  return uv_poll_init(loop, handle, socket);
+}
+
+
+static void uv__poll_stop(uv_poll_t* handle) {
+  uv__io_stop(handle->loop,
+              &handle->io_watcher,
+              POLLIN | POLLOUT | UV__POLLRDHUP | UV__POLLPRI);
+  uv__handle_stop(handle);
+  uv__platform_invalidate_fd(handle->loop, handle->io_watcher.fd);
+}
+
+
+int uv_poll_stop(uv_poll_t* handle) {
+  assert(!uv__is_closing(handle));
+  uv__poll_stop(handle);
+  return 0;
+}
+
+
+int uv_poll_start(uv_poll_t* handle, int pevents, uv_poll_cb poll_cb) {
+  void** watchers;
+  uv__io_t* w;
+  int events;
+
+  assert((pevents & ~(UV_READABLE | UV_WRITABLE | UV_DISCONNECT |
+                      UV_PRIORITIZED)) == 0);
+  assert(!uv__is_closing(handle));
+
+  watchers = handle->loop->watchers;
+  w = &handle->io_watcher;
+
+  if (uv__fd_exists(handle->loop, w->fd))
+    if (watchers[w->fd] != w)
+      return UV_EEXIST;
+
+  uv__poll_stop(handle);
+
+  if (pevents == 0)
+    return 0;
+
+  events = 0;
+  if (pevents & UV_READABLE)
+    events |= POLLIN;
+  if (pevents & UV_PRIORITIZED)
+    events |= UV__POLLPRI;
+  if (pevents & UV_WRITABLE)
+    events |= POLLOUT;
+  if (pevents & UV_DISCONNECT)
+    events |= UV__POLLRDHUP;
+
+  uv__io_start(handle->loop, &handle->io_watcher, events);
+  uv__handle_start(handle);
+  handle->poll_cb = poll_cb;
+
+  return 0;
+}
+
+
+void uv__poll_close(uv_poll_t* handle) {
+  uv__poll_stop(handle);
+}
diff --git a/wpinet/src/main/native/thirdparty/libuv/src/unix/posix-hrtime.cpp b/wpinet/src/main/native/thirdparty/libuv/src/unix/posix-hrtime.cpp
new file mode 100644
index 0000000..323dfc2
--- /dev/null
+++ b/wpinet/src/main/native/thirdparty/libuv/src/unix/posix-hrtime.cpp
@@ -0,0 +1,35 @@
+/* Copyright libuv project contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include "uv.h"
+#include "internal.h"
+
+#include <stdint.h>
+#include <time.h>
+
+#undef NANOSEC
+#define NANOSEC ((uint64_t) 1e9)
+
+uint64_t uv__hrtime(uv_clocktype_t type) {
+  struct timespec ts;
+  clock_gettime(CLOCK_MONOTONIC, &ts);
+  return (((uint64_t) ts.tv_sec) * NANOSEC + ts.tv_nsec);
+}
diff --git a/wpinet/src/main/native/thirdparty/libuv/src/unix/posix-poll.cpp b/wpinet/src/main/native/thirdparty/libuv/src/unix/posix-poll.cpp
new file mode 100644
index 0000000..8da038d
--- /dev/null
+++ b/wpinet/src/main/native/thirdparty/libuv/src/unix/posix-poll.cpp
@@ -0,0 +1,374 @@
+/* Copyright libuv project contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include "uv.h"
+#include "internal.h"
+
+/* POSIX defines poll() as a portable way to wait on file descriptors.
+ * Here we maintain a dynamically sized array of file descriptors and
+ * events to pass as the first argument to poll().
+ */
+
+#include <assert.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <errno.h>
+#include <unistd.h>
+
+int uv__platform_loop_init(uv_loop_t* loop) {
+  loop->poll_fds = NULL;
+  loop->poll_fds_used = 0;
+  loop->poll_fds_size = 0;
+  loop->poll_fds_iterating = 0;
+  return 0;
+}
+
+void uv__platform_loop_delete(uv_loop_t* loop) {
+  uv__free(loop->poll_fds);
+  loop->poll_fds = NULL;
+}
+
+int uv__io_fork(uv_loop_t* loop) {
+  uv__platform_loop_delete(loop);
+  return uv__platform_loop_init(loop);
+}
+
+/* Allocate or dynamically resize our poll fds array.  */
+static void uv__pollfds_maybe_resize(uv_loop_t* loop) {
+  size_t i;
+  size_t n;
+  struct pollfd* p;
+
+  if (loop->poll_fds_used < loop->poll_fds_size)
+    return;
+
+  n = loop->poll_fds_size ? loop->poll_fds_size * 2 : 64;
+  p = (struct pollfd*)uv__reallocf(loop->poll_fds, n * sizeof(*loop->poll_fds));
+  if (p == NULL)
+    abort();
+
+  loop->poll_fds = p;
+  for (i = loop->poll_fds_size; i < n; i++) {
+    loop->poll_fds[i].fd = -1;
+    loop->poll_fds[i].events = 0;
+    loop->poll_fds[i].revents = 0;
+  }
+  loop->poll_fds_size = n;
+}
+
+/* Primitive swap operation on poll fds array elements.  */
+static void uv__pollfds_swap(uv_loop_t* loop, size_t l, size_t r) {
+  struct pollfd pfd;
+  pfd = loop->poll_fds[l];
+  loop->poll_fds[l] = loop->poll_fds[r];
+  loop->poll_fds[r] = pfd;
+}
+
+/* Add a watcher's fd to our poll fds array with its pending events.  */
+static void uv__pollfds_add(uv_loop_t* loop, uv__io_t* w) {
+  size_t i;
+  struct pollfd* pe;
+
+  /* If the fd is already in the set just update its events.  */
+  assert(!loop->poll_fds_iterating);
+  for (i = 0; i < loop->poll_fds_used; ++i) {
+    if (loop->poll_fds[i].fd == w->fd) {
+      loop->poll_fds[i].events = w->pevents;
+      return;
+    }
+  }
+
+  /* Otherwise, allocate a new slot in the set for the fd.  */
+  uv__pollfds_maybe_resize(loop);
+  pe = &loop->poll_fds[loop->poll_fds_used++];
+  pe->fd = w->fd;
+  pe->events = w->pevents;
+}
+
+/* Remove a watcher's fd from our poll fds array.  */
+static void uv__pollfds_del(uv_loop_t* loop, int fd) {
+  size_t i;
+  assert(!loop->poll_fds_iterating);
+  for (i = 0; i < loop->poll_fds_used;) {
+    if (loop->poll_fds[i].fd == fd) {
+      /* swap to last position and remove */
+      --loop->poll_fds_used;
+      uv__pollfds_swap(loop, i, loop->poll_fds_used);
+      loop->poll_fds[loop->poll_fds_used].fd = -1;
+      loop->poll_fds[loop->poll_fds_used].events = 0;
+      loop->poll_fds[loop->poll_fds_used].revents = 0;
+      /* This method is called with an fd of -1 to purge the invalidated fds,
+       * so we may possibly have multiples to remove.
+       */
+      if (-1 != fd)
+        return;
+    } else {
+      /* We must only increment the loop counter when the fds do not match.
+       * Otherwise, when we are purging an invalidated fd, the value just
+       * swapped here from the previous end of the array will be skipped.
+       */
+       ++i;
+    }
+  }
+}
+
+
+void uv__io_poll(uv_loop_t* loop, int timeout) {
+  sigset_t* pset;
+  sigset_t set;
+  uint64_t time_base;
+  uint64_t time_diff;
+  QUEUE* q;
+  uv__io_t* w;
+  size_t i;
+  unsigned int nevents;
+  int nfds;
+  int have_signals;
+  struct pollfd* pe;
+  int fd;
+  int user_timeout;
+  int reset_timeout;
+
+  if (loop->nfds == 0) {
+    assert(QUEUE_EMPTY(&loop->watcher_queue));
+    return;
+  }
+
+  /* Take queued watchers and add their fds to our poll fds array.  */
+  while (!QUEUE_EMPTY(&loop->watcher_queue)) {
+    q = QUEUE_HEAD(&loop->watcher_queue);
+    QUEUE_REMOVE(q);
+    QUEUE_INIT(q);
+
+    w = QUEUE_DATA(q, uv__io_t, watcher_queue);
+    assert(w->pevents != 0);
+    assert(w->fd >= 0);
+    assert(w->fd < (int) loop->nwatchers);
+
+    uv__pollfds_add(loop, w);
+
+    w->events = w->pevents;
+  }
+
+  /* Prepare a set of signals to block around poll(), if any.  */
+  pset = NULL;
+  if (loop->flags & UV_LOOP_BLOCK_SIGPROF) {
+    pset = &set;
+    sigemptyset(pset);
+    sigaddset(pset, SIGPROF);
+  }
+
+  assert(timeout >= -1);
+  time_base = loop->time;
+
+  if (uv__get_internal_fields(loop)->flags & UV_METRICS_IDLE_TIME) {
+    reset_timeout = 1;
+    user_timeout = timeout;
+    timeout = 0;
+  } else {
+    reset_timeout = 0;
+  }
+
+  /* Loop calls to poll() and processing of results.  If we get some
+   * results from poll() but they turn out not to be interesting to
+   * our caller then we need to loop around and poll() again.
+   */
+  for (;;) {
+    /* Only need to set the provider_entry_time if timeout != 0. The function
+     * will return early if the loop isn't configured with UV_METRICS_IDLE_TIME.
+     */
+    if (timeout != 0)
+      uv__metrics_set_provider_entry_time(loop);
+
+    if (pset != NULL)
+      if (pthread_sigmask(SIG_BLOCK, pset, NULL))
+        abort();
+    nfds = poll(loop->poll_fds, (nfds_t)loop->poll_fds_used, timeout);
+    if (pset != NULL)
+      if (pthread_sigmask(SIG_UNBLOCK, pset, NULL))
+        abort();
+
+    /* Update loop->time unconditionally. It's tempting to skip the update when
+     * timeout == 0 (i.e. non-blocking poll) but there is no guarantee that the
+     * operating system didn't reschedule our process while in the syscall.
+     */
+    SAVE_ERRNO(uv__update_time(loop));
+
+    if (nfds == 0) {
+      if (reset_timeout != 0) {
+        timeout = user_timeout;
+        reset_timeout = 0;
+        if (timeout == -1)
+          continue;
+        if (timeout > 0)
+          goto update_timeout;
+      }
+
+      assert(timeout != -1);
+      return;
+    }
+
+    if (nfds == -1) {
+      if (errno != EINTR)
+        abort();
+
+      if (reset_timeout != 0) {
+        timeout = user_timeout;
+        reset_timeout = 0;
+      }
+
+      if (timeout == -1)
+        continue;
+
+      if (timeout == 0)
+        return;
+
+      /* Interrupted by a signal. Update timeout and poll again. */
+      goto update_timeout;
+    }
+
+    /* Tell uv__platform_invalidate_fd not to manipulate our array
+     * while we are iterating over it.
+     */
+    loop->poll_fds_iterating = 1;
+
+    /* Initialize a count of events that we care about.  */
+    nevents = 0;
+    have_signals = 0;
+
+    /* Loop over the entire poll fds array looking for returned events.  */
+    for (i = 0; i < loop->poll_fds_used; i++) {
+      pe = loop->poll_fds + i;
+      fd = pe->fd;
+
+      /* Skip invalidated events, see uv__platform_invalidate_fd.  */
+      if (fd == -1)
+        continue;
+
+      assert(fd >= 0);
+      assert((unsigned) fd < loop->nwatchers);
+
+      w = loop->watchers[fd];
+
+      if (w == NULL) {
+        /* File descriptor that we've stopped watching, ignore.  */
+        uv__platform_invalidate_fd(loop, fd);
+        continue;
+      }
+
+      /* Filter out events that user has not requested us to watch
+       * (e.g. POLLNVAL).
+       */
+      pe->revents &= w->pevents | POLLERR | POLLHUP;
+
+      if (pe->revents != 0) {
+        /* Run signal watchers last.  */
+        if (w == &loop->signal_io_watcher) {
+          have_signals = 1;
+        } else {
+          uv__metrics_update_idle_time(loop);
+          w->cb(loop, w, pe->revents);
+        }
+
+        nevents++;
+      }
+    }
+
+    if (reset_timeout != 0) {
+      timeout = user_timeout;
+      reset_timeout = 0;
+    }
+
+    if (have_signals != 0) {
+      uv__metrics_update_idle_time(loop);
+      loop->signal_io_watcher.cb(loop, &loop->signal_io_watcher, POLLIN);
+    }
+
+    loop->poll_fds_iterating = 0;
+
+    /* Purge invalidated fds from our poll fds array.  */
+    uv__pollfds_del(loop, -1);
+
+    if (have_signals != 0)
+      return;  /* Event loop should cycle now so don't poll again. */
+
+    if (nevents != 0)
+      return;
+
+    if (timeout == 0)
+      return;
+
+    if (timeout == -1)
+      continue;
+
+update_timeout:
+    assert(timeout > 0);
+
+    time_diff = loop->time - time_base;
+    if (time_diff >= (uint64_t) timeout)
+      return;
+
+    timeout -= time_diff;
+  }
+}
+
+/* Remove the given fd from our poll fds array because no one
+ * is interested in its events anymore.
+ */
+void uv__platform_invalidate_fd(uv_loop_t* loop, int fd) {
+  size_t i;
+
+  assert(fd >= 0);
+
+  if (loop->poll_fds_iterating) {
+    /* uv__io_poll is currently iterating.  Just invalidate fd.  */
+    for (i = 0; i < loop->poll_fds_used; i++)
+      if (loop->poll_fds[i].fd == fd) {
+        loop->poll_fds[i].fd = -1;
+        loop->poll_fds[i].events = 0;
+        loop->poll_fds[i].revents = 0;
+      }
+  } else {
+    /* uv__io_poll is not iterating.  Delete fd from the set.  */
+    uv__pollfds_del(loop, fd);
+  }
+}
+
+/* Check whether the given fd is supported by poll().  */
+int uv__io_check_fd(uv_loop_t* loop, int fd) {
+  struct pollfd p[1];
+  int rv;
+
+  p[0].fd = fd;
+  p[0].events = POLLIN;
+
+  do
+    rv = poll(p, 1, 0);
+  while (rv == -1 && (errno == EINTR || errno == EAGAIN));
+
+  if (rv == -1)
+    return UV__ERR(errno);
+
+  if (p[0].revents & POLLNVAL)
+    return UV_EINVAL;
+
+  return 0;
+}
diff --git a/wpinet/src/main/native/thirdparty/libuv/src/unix/process.cpp b/wpinet/src/main/native/thirdparty/libuv/src/unix/process.cpp
new file mode 100644
index 0000000..0916aa4
--- /dev/null
+++ b/wpinet/src/main/native/thirdparty/libuv/src/unix/process.cpp
@@ -0,0 +1,1085 @@
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include "uv.h"
+#include "internal.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <errno.h>
+#include <signal.h>
+#include <string.h>
+
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <poll.h>
+
+#if defined(__APPLE__)
+# include <spawn.h>
+# include <paths.h>
+# include <sys/kauth.h>
+# include <sys/types.h>
+# include <sys/sysctl.h>
+# include <dlfcn.h>
+# include <crt_externs.h>
+# include <xlocale.h>
+# define environ (*_NSGetEnviron())
+
+/* macOS 10.14 back does not define this constant */
+# ifndef POSIX_SPAWN_SETSID
+#  define POSIX_SPAWN_SETSID 1024
+# endif
+
+#else
+extern char **environ;
+#endif
+
+#if defined(__linux__) || defined(__GLIBC__)
+# include <grp.h>
+#endif
+
+#if defined(__MVS__)
+# include "zos-base.h"
+#endif
+
+#if defined(__APPLE__) || \
+    defined(__DragonFly__) || \
+    defined(__FreeBSD__) || \
+    defined(__NetBSD__) || \
+    defined(__OpenBSD__)
+#include <sys/event.h>
+#else
+#define UV_USE_SIGCHLD
+#endif
+
+
+#ifdef UV_USE_SIGCHLD
+static void uv__chld(uv_signal_t* handle, int signum) {
+  assert(signum == SIGCHLD);
+  uv__wait_children(handle->loop);
+}
+#endif
+
+void uv__wait_children(uv_loop_t* loop) {
+  uv_process_t* process;
+  int exit_status;
+  int term_signal;
+  int status;
+  int options;
+  pid_t pid;
+  QUEUE pending;
+  QUEUE* q;
+  QUEUE* h;
+
+  QUEUE_INIT(&pending);
+
+  h = &loop->process_handles;
+  q = QUEUE_HEAD(h);
+  while (q != h) {
+    process = QUEUE_DATA(q, uv_process_t, queue);
+    q = QUEUE_NEXT(q);
+
+#ifndef UV_USE_SIGCHLD
+    if ((process->flags & UV_HANDLE_REAP) == 0)
+      continue;
+    options = 0;
+    process->flags &= ~UV_HANDLE_REAP;
+#else
+    options = WNOHANG;
+#endif
+
+    do
+      pid = waitpid(process->pid, &status, options);
+    while (pid == -1 && errno == EINTR);
+
+#ifdef UV_USE_SIGCHLD
+    if (pid == 0) /* Not yet exited */
+      continue;
+#endif
+
+    if (pid == -1) {
+      if (errno != ECHILD)
+        abort();
+      /* The child died, and we missed it. This probably means someone else
+       * stole the waitpid from us. Handle this by not handling it at all. */
+      continue;
+    }
+
+    assert(pid == process->pid);
+    process->status = status;
+    QUEUE_REMOVE(&process->queue);
+    QUEUE_INSERT_TAIL(&pending, &process->queue);
+  }
+
+  h = &pending;
+  q = QUEUE_HEAD(h);
+  while (q != h) {
+    process = QUEUE_DATA(q, uv_process_t, queue);
+    q = QUEUE_NEXT(q);
+
+    QUEUE_REMOVE(&process->queue);
+    QUEUE_INIT(&process->queue);
+    uv__handle_stop(process);
+
+    if (process->exit_cb == NULL)
+      continue;
+
+    exit_status = 0;
+    if (WIFEXITED(process->status))
+      exit_status = WEXITSTATUS(process->status);
+
+    term_signal = 0;
+    if (WIFSIGNALED(process->status))
+      term_signal = WTERMSIG(process->status);
+
+    process->exit_cb(process, exit_status, term_signal);
+  }
+  assert(QUEUE_EMPTY(&pending));
+}
+
+/*
+ * Used for initializing stdio streams like options.stdin_stream. Returns
+ * zero on success. See also the cleanup section in uv_spawn().
+ */
+static int uv__process_init_stdio(uv_stdio_container_t* container, int fds[2]) {
+  int mask;
+  int fd;
+
+  mask = UV_IGNORE | UV_CREATE_PIPE | UV_INHERIT_FD | UV_INHERIT_STREAM;
+
+  switch (container->flags & mask) {
+  case UV_IGNORE:
+    return 0;
+
+  case UV_CREATE_PIPE:
+    assert(container->data.stream != NULL);
+    if (container->data.stream->type != UV_NAMED_PIPE)
+      return UV_EINVAL;
+    else
+      return uv_socketpair(SOCK_STREAM, 0, fds, 0, 0);
+
+  case UV_INHERIT_FD:
+  case UV_INHERIT_STREAM:
+    if (container->flags & UV_INHERIT_FD)
+      fd = container->data.fd;
+    else
+      fd = uv__stream_fd(container->data.stream);
+
+    if (fd == -1)
+      return UV_EINVAL;
+
+    fds[1] = fd;
+    return 0;
+
+  default:
+    assert(0 && "Unexpected flags");
+    return UV_EINVAL;
+  }
+}
+
+
+static int uv__process_open_stream(uv_stdio_container_t* container,
+                                   int pipefds[2]) {
+  int flags;
+  int err;
+
+  if (!(container->flags & UV_CREATE_PIPE) || pipefds[0] < 0)
+    return 0;
+
+  err = uv__close(pipefds[1]);
+  if (err != 0)
+    abort();
+
+  pipefds[1] = -1;
+  uv__nonblock(pipefds[0], 1);
+
+  flags = 0;
+  if (container->flags & UV_WRITABLE_PIPE)
+    flags |= UV_HANDLE_READABLE;
+  if (container->flags & UV_READABLE_PIPE)
+    flags |= UV_HANDLE_WRITABLE;
+
+  return uv__stream_open(container->data.stream, pipefds[0], flags);
+}
+
+
+static void uv__process_close_stream(uv_stdio_container_t* container) {
+  if (!(container->flags & UV_CREATE_PIPE)) return;
+  uv__stream_close(container->data.stream);
+}
+
+
+static void uv__write_int(int fd, int val) {
+  ssize_t n;
+
+  do
+    n = write(fd, &val, sizeof(val));
+  while (n == -1 && errno == EINTR);
+
+  /* The write might have failed (e.g. if the parent process has died),
+   * but we have nothing left but to _exit ourself now too. */
+  _exit(127);
+}
+
+
+static void uv__write_errno(int error_fd) {
+  uv__write_int(error_fd, UV__ERR(errno));
+}
+
+
+#if !(defined(__APPLE__) && (TARGET_OS_TV || TARGET_OS_WATCH))
+/* execvp is marked __WATCHOS_PROHIBITED __TVOS_PROHIBITED, so must be
+ * avoided. Since this isn't called on those targets, the function
+ * doesn't even need to be defined for them.
+ */
+static void uv__process_child_init(const uv_process_options_t* options,
+                                   int stdio_count,
+                                   int (*pipes)[2],
+                                   int error_fd) {
+  sigset_t signewset;
+  int close_fd;
+  int use_fd;
+  int fd;
+  int n;
+
+  /* Reset signal disposition first. Use a hard-coded limit because NSIG is not
+   * fixed on Linux: it's either 32, 34 or 64, depending on whether RT signals
+   * are enabled. We are not allowed to touch RT signal handlers, glibc uses
+   * them internally.
+   */
+  for (n = 1; n < 32; n += 1) {
+    if (n == SIGKILL || n == SIGSTOP)
+      continue;  /* Can't be changed. */
+
+#if defined(__HAIKU__)
+    if (n == SIGKILLTHR)
+      continue;  /* Can't be changed. */
+#endif
+
+    if (SIG_ERR != signal(n, SIG_DFL))
+      continue;
+
+    uv__write_errno(error_fd);
+  }
+
+  if (options->flags & UV_PROCESS_DETACHED)
+    setsid();
+
+  /* First duplicate low numbered fds, since it's not safe to duplicate them,
+   * they could get replaced. Example: swapping stdout and stderr; without
+   * this fd 2 (stderr) would be duplicated into fd 1, thus making both
+   * stdout and stderr go to the same fd, which was not the intention. */
+  for (fd = 0; fd < stdio_count; fd++) {
+    use_fd = pipes[fd][1];
+    if (use_fd < 0 || use_fd >= fd)
+      continue;
+#ifdef F_DUPFD_CLOEXEC /* POSIX 2008 */
+    pipes[fd][1] = fcntl(use_fd, F_DUPFD_CLOEXEC, stdio_count);
+#else
+    pipes[fd][1] = fcntl(use_fd, F_DUPFD, stdio_count);
+#endif
+    if (pipes[fd][1] == -1)
+      uv__write_errno(error_fd);
+#ifndef F_DUPFD_CLOEXEC /* POSIX 2008 */
+    n = uv__cloexec(pipes[fd][1], 1);
+    if (n)
+      uv__write_int(error_fd, n);
+#endif
+  }
+
+  for (fd = 0; fd < stdio_count; fd++) {
+    close_fd = -1;
+    use_fd = pipes[fd][1];
+
+    if (use_fd < 0) {
+      if (fd >= 3)
+        continue;
+      else {
+        /* Redirect stdin, stdout and stderr to /dev/null even if UV_IGNORE is
+         * set. */
+        uv__close_nocheckstdio(fd); /* Free up fd, if it happens to be open. */
+        use_fd = open("/dev/null", fd == 0 ? O_RDONLY : O_RDWR);
+        close_fd = use_fd;
+
+        if (use_fd < 0)
+          uv__write_errno(error_fd);
+      }
+    }
+
+    if (fd == use_fd) {
+      if (close_fd == -1) {
+        n = uv__cloexec(use_fd, 0);
+        if (n)
+          uv__write_int(error_fd, n);
+      }
+    }
+    else {
+      fd = dup2(use_fd, fd);
+    }
+
+    if (fd == -1)
+      uv__write_errno(error_fd);
+
+    if (fd <= 2 && close_fd == -1)
+      uv__nonblock_fcntl(fd, 0);
+
+    if (close_fd >= stdio_count)
+      uv__close(close_fd);
+  }
+
+  if (options->cwd != NULL && chdir(options->cwd))
+    uv__write_errno(error_fd);
+
+  if (options->flags & (UV_PROCESS_SETUID | UV_PROCESS_SETGID)) {
+    /* When dropping privileges from root, the `setgroups` call will
+     * remove any extraneous groups. If we don't call this, then
+     * even though our uid has dropped, we may still have groups
+     * that enable us to do super-user things. This will fail if we
+     * aren't root, so don't bother checking the return value, this
+     * is just done as an optimistic privilege dropping function.
+     */
+    SAVE_ERRNO(setgroups(0, NULL));
+  }
+
+  if ((options->flags & UV_PROCESS_SETGID) && setgid(options->gid))
+    uv__write_errno(error_fd);
+
+  if ((options->flags & UV_PROCESS_SETUID) && setuid(options->uid))
+    uv__write_errno(error_fd);
+
+  if (options->env != NULL)
+    environ = options->env;
+
+  /* Reset signal mask just before exec. */
+  sigemptyset(&signewset);
+  if (sigprocmask(SIG_SETMASK, &signewset, NULL) != 0)
+    abort();
+
+#ifdef __MVS__
+  execvpe(options->file, options->args, environ);
+#else
+  execvp(options->file, options->args);
+#endif
+
+  uv__write_errno(error_fd);
+}
+#endif
+
+
+#if defined(__APPLE__)
+typedef struct uv__posix_spawn_fncs_tag {
+  struct {
+    int (*addchdir_np)(const posix_spawn_file_actions_t *, const char *);
+  } file_actions;
+} uv__posix_spawn_fncs_t;
+
+
+static uv_once_t posix_spawn_init_once = UV_ONCE_INIT;
+static uv__posix_spawn_fncs_t posix_spawn_fncs;
+static int posix_spawn_can_use_setsid;
+
+
+static void uv__spawn_init_posix_spawn_fncs(void) {
+  /* Try to locate all non-portable functions at runtime */
+  posix_spawn_fncs.file_actions.addchdir_np =
+    (int (*)(void* const*, const char*)) dlsym(RTLD_DEFAULT, "posix_spawn_file_actions_addchdir_np");
+}
+
+
+static void uv__spawn_init_can_use_setsid(void) {
+  int which[] = {CTL_KERN, KERN_OSRELEASE};
+  unsigned major;
+  unsigned minor;
+  unsigned patch;
+  char buf[256];
+  size_t len;
+
+  len = sizeof(buf);
+  if (sysctl(which, ARRAY_SIZE(which), buf, &len, NULL, 0))
+    return;
+
+  /* NULL specifies to use LC_C_LOCALE */
+  if (3 != sscanf_l(buf, NULL, "%u.%u.%u", &major, &minor, &patch))
+    return;
+
+  posix_spawn_can_use_setsid = (major >= 19);  /* macOS Catalina */
+}
+
+
+static void uv__spawn_init_posix_spawn(void) {
+  /* Init handles to all potentially non-defined functions */
+  uv__spawn_init_posix_spawn_fncs();
+
+  /* Init feature detection for POSIX_SPAWN_SETSID flag */
+  uv__spawn_init_can_use_setsid();
+}
+
+
+static int uv__spawn_set_posix_spawn_attrs(
+    posix_spawnattr_t* attrs,
+    const uv__posix_spawn_fncs_t* posix_spawn_fncs,
+    const uv_process_options_t* options) {
+  int err;
+  unsigned int flags;
+  sigset_t signal_set;
+
+  err = posix_spawnattr_init(attrs);
+  if (err != 0) {
+    /* If initialization fails, no need to de-init, just return */
+    return err;
+  }
+
+  if (options->flags & (UV_PROCESS_SETUID | UV_PROCESS_SETGID)) {
+    /* kauth_cred_issuser currently requires exactly uid == 0 for these
+     * posixspawn_attrs (set_groups_np, setuid_np, setgid_np), which deviates
+     * from the normal specification of setuid (which also uses euid), and they
+     * are also undocumented syscalls, so we do not use them. */
+    err = ENOSYS;
+    goto error;
+  }
+
+  /* Set flags for spawn behavior
+   * 1) POSIX_SPAWN_CLOEXEC_DEFAULT: (Apple Extension) All descriptors in the
+   *    parent will be treated as if they had been created with O_CLOEXEC. The
+   *    only fds that will be passed on to the child are those manipulated by
+   *    the file actions
+   * 2) POSIX_SPAWN_SETSIGDEF: Signals mentioned in spawn-sigdefault in the
+   *    spawn attributes will be reset to behave as their default
+   * 3) POSIX_SPAWN_SETSIGMASK: Signal mask will be set to the value of
+   *    spawn-sigmask in attributes
+   * 4) POSIX_SPAWN_SETSID: Make the process a new session leader if a detached
+   *    session was requested. */
+  flags = POSIX_SPAWN_CLOEXEC_DEFAULT |
+          POSIX_SPAWN_SETSIGDEF |
+          POSIX_SPAWN_SETSIGMASK;
+  if (options->flags & UV_PROCESS_DETACHED) {
+    /* If running on a version of macOS where this flag is not supported,
+     * revert back to the fork/exec flow. Otherwise posix_spawn will
+     * silently ignore the flag. */
+    if (!posix_spawn_can_use_setsid) {
+      err = ENOSYS;
+      goto error;
+    }
+
+    flags |= POSIX_SPAWN_SETSID;
+  }
+  err = posix_spawnattr_setflags(attrs, flags);
+  if (err != 0)
+    goto error;
+
+  /* Reset all signal the child to their default behavior */
+  sigfillset(&signal_set);
+  err = posix_spawnattr_setsigdefault(attrs, &signal_set);
+  if (err != 0)
+    goto error;
+
+  /* Reset the signal mask for all signals */
+  sigemptyset(&signal_set);
+  err = posix_spawnattr_setsigmask(attrs, &signal_set);
+  if (err != 0)
+    goto error;
+
+  return err;
+
+error:
+  (void) posix_spawnattr_destroy(attrs);
+  return err;
+}
+
+
+static int uv__spawn_set_posix_spawn_file_actions(
+    posix_spawn_file_actions_t* actions,
+    const uv__posix_spawn_fncs_t* posix_spawn_fncs,
+    const uv_process_options_t* options,
+    int stdio_count,
+    int (*pipes)[2]) {
+  int fd;
+  int fd2;
+  int use_fd;
+  int err;
+
+  err = posix_spawn_file_actions_init(actions);
+  if (err != 0) {
+    /* If initialization fails, no need to de-init, just return */
+    return err;
+  }
+
+  /* Set the current working directory if requested */
+  if (options->cwd != NULL) {
+    if (posix_spawn_fncs->file_actions.addchdir_np == NULL) {
+      err = ENOSYS;
+      goto error;
+    }
+
+    err = posix_spawn_fncs->file_actions.addchdir_np(actions, options->cwd);
+    if (err != 0)
+      goto error;
+  }
+
+  /* Do not return ENOSYS after this point, as we may mutate pipes. */
+
+  /* First duplicate low numbered fds, since it's not safe to duplicate them,
+   * they could get replaced. Example: swapping stdout and stderr; without
+   * this fd 2 (stderr) would be duplicated into fd 1, thus making both
+   * stdout and stderr go to the same fd, which was not the intention. */
+  for (fd = 0; fd < stdio_count; fd++) {
+    use_fd = pipes[fd][1];
+    if (use_fd < 0 || use_fd >= fd)
+      continue;
+    use_fd = stdio_count;
+    for (fd2 = 0; fd2 < stdio_count; fd2++) {
+      /* If we were not setting POSIX_SPAWN_CLOEXEC_DEFAULT, we would need to
+       * also consider whether fcntl(fd, F_GETFD) returned without the
+       * FD_CLOEXEC flag set. */
+      if (pipes[fd2][1] == use_fd) {
+        use_fd++;
+        fd2 = 0;
+      }
+    }
+    err = posix_spawn_file_actions_adddup2(
+      actions,
+      pipes[fd][1],
+      use_fd);
+    assert(err != ENOSYS);
+    if (err != 0)
+      goto error;
+    pipes[fd][1] = use_fd;
+  }
+
+  /* Second, move the descriptors into their respective places */
+  for (fd = 0; fd < stdio_count; fd++) {
+    use_fd = pipes[fd][1];
+    if (use_fd < 0) {
+      if (fd >= 3)
+        continue;
+      else {
+        /* If ignored, redirect to (or from) /dev/null, */
+        err = posix_spawn_file_actions_addopen(
+          actions,
+          fd,
+          "/dev/null",
+          fd == 0 ? O_RDONLY : O_RDWR,
+          0);
+        assert(err != ENOSYS);
+        if (err != 0)
+          goto error;
+        continue;
+      }
+    }
+
+    if (fd == use_fd)
+        err = posix_spawn_file_actions_addinherit_np(actions, fd);
+    else
+        err = posix_spawn_file_actions_adddup2(actions, use_fd, fd);
+    assert(err != ENOSYS);
+    if (err != 0)
+      goto error;
+
+    /* Make sure the fd is marked as non-blocking (state shared between child
+     * and parent). */
+    uv__nonblock_fcntl(use_fd, 0);
+  }
+
+  /* Finally, close all the superfluous descriptors */
+  for (fd = 0; fd < stdio_count; fd++) {
+    use_fd = pipes[fd][1];
+    if (use_fd < stdio_count)
+      continue;
+
+    /* Check if we already closed this. */
+    for (fd2 = 0; fd2 < fd; fd2++) {
+      if (pipes[fd2][1] == use_fd)
+          break;
+    }
+    if (fd2 < fd)
+      continue;
+
+    err = posix_spawn_file_actions_addclose(actions, use_fd);
+    assert(err != ENOSYS);
+    if (err != 0)
+      goto error;
+  }
+
+  return 0;
+
+error:
+  (void) posix_spawn_file_actions_destroy(actions);
+  return err;
+}
+
+char* uv__spawn_find_path_in_env(char** env) {
+  char** env_iterator;
+  const char path_var[] = "PATH=";
+
+  /* Look for an environment variable called PATH in the
+   * provided env array, and return its value if found */
+  for (env_iterator = env; *env_iterator != NULL; env_iterator++) {
+    if (strncmp(*env_iterator, path_var, sizeof(path_var) - 1) == 0) {
+      /* Found "PATH=" at the beginning of the string */
+      return *env_iterator + sizeof(path_var) - 1;
+    }
+  }
+
+  return NULL;
+}
+
+
+static int uv__spawn_resolve_and_spawn(const uv_process_options_t* options,
+                                       posix_spawnattr_t* attrs,
+                                       posix_spawn_file_actions_t* actions,
+                                       pid_t* pid) {
+  const char *p;
+  const char *z;
+  const char *path;
+  size_t l;
+  size_t k;
+  int err;
+  int seen_eacces;
+
+  path = NULL;
+  err = -1;
+  seen_eacces = 0;
+
+  /* Short circuit for erroneous case */
+  if (options->file == NULL)
+    return ENOENT;
+
+  /* The environment for the child process is that of the parent unless overriden
+   * by options->env */
+  char** env = environ;
+  if (options->env != NULL)
+    env = options->env;
+
+  /* If options->file contains a slash, posix_spawn/posix_spawnp should behave
+   * the same, and do not involve PATH resolution at all. The libc
+   * `posix_spawnp` provided by Apple is buggy (since 10.15), so we now emulate it
+   * here, per https://github.com/libuv/libuv/pull/3583. */
+  if (strchr(options->file, '/') != NULL) {
+    do
+      err = posix_spawn(pid, options->file, actions, attrs, options->args, env);
+    while (err == EINTR);
+    return err;
+  }
+
+  /* Look for the definition of PATH in the provided env */
+  path = uv__spawn_find_path_in_env(env);
+
+  /* The following resolution logic (execvpe emulation) is copied from
+   * https://git.musl-libc.org/cgit/musl/tree/src/process/execvp.c
+   * and adapted to work for our specific usage */
+
+  /* If no path was provided in env, use the default value
+   * to look for the executable */
+  if (path == NULL)
+    path = _PATH_DEFPATH;
+
+  k = strnlen(options->file, NAME_MAX + 1);
+  if (k > NAME_MAX)
+    return ENAMETOOLONG;
+
+  l = strnlen(path, PATH_MAX - 1) + 1;
+
+  for (p = path;; p = z) {
+    /* Compose the new process file from the entry in the PATH
+     * environment variable and the actual file name */
+    char b[PATH_MAX + NAME_MAX];
+    z = strchr(p, ':');
+    if (!z)
+      z = p + strlen(p);
+    if ((size_t)(z - p) >= l) {
+      if (!*z++)
+        break;
+
+      continue;
+    }
+    memcpy(b, p, z - p);
+    b[z - p] = '/';
+    memcpy(b + (z - p) + (z > p), options->file, k + 1);
+
+    /* Try to spawn the new process file. If it fails with ENOENT, the
+     * new process file is not in this PATH entry, continue with the next
+     * PATH entry. */
+    do
+      err = posix_spawn(pid, b, actions, attrs, options->args, env);
+    while (err == EINTR);
+
+    switch (err) {
+    case EACCES:
+      seen_eacces = 1;
+      break; /* continue search */
+    case ENOENT:
+    case ENOTDIR:
+      break; /* continue search */
+    default:
+      return err;
+    }
+
+    if (!*z++)
+      break;
+  }
+
+  if (seen_eacces)
+    return EACCES;
+  return err;
+}
+
+
+static int uv__spawn_and_init_child_posix_spawn(
+    const uv_process_options_t* options,
+    int stdio_count,
+    int (*pipes)[2],
+    pid_t* pid,
+    const uv__posix_spawn_fncs_t* posix_spawn_fncs) {
+  int err;
+  posix_spawnattr_t attrs;
+  posix_spawn_file_actions_t actions;
+
+  err = uv__spawn_set_posix_spawn_attrs(&attrs, posix_spawn_fncs, options);
+  if (err != 0)
+    goto error;
+
+  /* This may mutate pipes. */
+  err = uv__spawn_set_posix_spawn_file_actions(&actions,
+                                               posix_spawn_fncs,
+                                               options,
+                                               stdio_count,
+                                               pipes);
+  if (err != 0) {
+    (void) posix_spawnattr_destroy(&attrs);
+    goto error;
+  }
+
+  /* Try to spawn options->file resolving in the provided environment
+   * if any */
+  err = uv__spawn_resolve_and_spawn(options, &attrs, &actions, pid);
+  assert(err != ENOSYS);
+
+  /* Destroy the actions/attributes */
+  (void) posix_spawn_file_actions_destroy(&actions);
+  (void) posix_spawnattr_destroy(&attrs);
+
+error:
+  /* In an error situation, the attributes and file actions are
+   * already destroyed, only the happy path requires cleanup */
+  return UV__ERR(err);
+}
+#endif
+
+static int uv__spawn_and_init_child_fork(const uv_process_options_t* options,
+                                         int stdio_count,
+                                         int (*pipes)[2],
+                                         int error_fd,
+                                         pid_t* pid) {
+  sigset_t signewset;
+  sigset_t sigoldset;
+
+  /* Start the child with most signals blocked, to avoid any issues before we
+   * can reset them, but allow program failures to exit (and not hang). */
+  sigfillset(&signewset);
+  sigdelset(&signewset, SIGKILL);
+  sigdelset(&signewset, SIGSTOP);
+  sigdelset(&signewset, SIGTRAP);
+  sigdelset(&signewset, SIGSEGV);
+  sigdelset(&signewset, SIGBUS);
+  sigdelset(&signewset, SIGILL);
+  sigdelset(&signewset, SIGSYS);
+  sigdelset(&signewset, SIGABRT);
+  if (pthread_sigmask(SIG_BLOCK, &signewset, &sigoldset) != 0)
+    abort();
+
+  *pid = fork();
+
+  if (*pid == 0) {
+    /* Fork succeeded, in the child process */
+    uv__process_child_init(options, stdio_count, pipes, error_fd);
+    abort();
+  }
+
+  if (pthread_sigmask(SIG_SETMASK, &sigoldset, NULL) != 0)
+    abort();
+
+  if (*pid == -1)
+    /* Failed to fork */
+    return UV__ERR(errno);
+
+  /* Fork succeeded, in the parent process */
+  return 0;
+}
+
+static int uv__spawn_and_init_child(
+    uv_loop_t* loop,
+    const uv_process_options_t* options,
+    int stdio_count,
+    int (*pipes)[2],
+    pid_t* pid) {
+  int signal_pipe[2] = { -1, -1 };
+  int status;
+  int err;
+  int exec_errorno;
+  ssize_t r;
+
+#if defined(__APPLE__)
+  uv_once(&posix_spawn_init_once, uv__spawn_init_posix_spawn);
+
+  /* Special child process spawn case for macOS Big Sur (11.0) onwards
+   *
+   * Big Sur introduced a significant performance degradation on a call to
+   * fork/exec when the process has many pages mmaped in with MAP_JIT, like, say
+   * a javascript interpreter. Electron-based applications, for example,
+   * are impacted; though the magnitude of the impact depends on how much the
+   * app relies on subprocesses.
+   *
+   * On macOS, though, posix_spawn is implemented in a way that does not
+   * exhibit the problem. This block implements the forking and preparation
+   * logic with posix_spawn and its related primitives. It also takes advantage of
+   * the macOS extension POSIX_SPAWN_CLOEXEC_DEFAULT that makes impossible to
+   * leak descriptors to the child process. */
+  err = uv__spawn_and_init_child_posix_spawn(options,
+                                             stdio_count,
+                                             pipes,
+                                             pid,
+                                             &posix_spawn_fncs);
+
+  /* The posix_spawn flow will return UV_ENOSYS if any of the posix_spawn_x_np
+   * non-standard functions is both _needed_ and _undefined_. In those cases,
+   * default back to the fork/execve strategy. For all other errors, just fail. */
+  if (err != UV_ENOSYS)
+    return err;
+
+#endif
+
+  /* This pipe is used by the parent to wait until
+   * the child has called `execve()`. We need this
+   * to avoid the following race condition:
+   *
+   *    if ((pid = fork()) > 0) {
+   *      kill(pid, SIGTERM);
+   *    }
+   *    else if (pid == 0) {
+   *      execve("/bin/cat", argp, envp);
+   *    }
+   *
+   * The parent sends a signal immediately after forking.
+   * Since the child may not have called `execve()` yet,
+   * there is no telling what process receives the signal,
+   * our fork or /bin/cat.
+   *
+   * To avoid ambiguity, we create a pipe with both ends
+   * marked close-on-exec. Then, after the call to `fork()`,
+   * the parent polls the read end until it EOFs or errors with EPIPE.
+   */
+  err = uv__make_pipe(signal_pipe, 0);
+  if (err)
+    return err;
+
+  /* Acquire write lock to prevent opening new fds in worker threads */
+  uv_rwlock_wrlock(&loop->cloexec_lock);
+
+  err = uv__spawn_and_init_child_fork(options, stdio_count, pipes, signal_pipe[1], pid);
+
+  /* Release lock in parent process */
+  uv_rwlock_wrunlock(&loop->cloexec_lock);
+
+  uv__close(signal_pipe[1]);
+
+  if (err == 0) {
+    do
+      r = read(signal_pipe[0], &exec_errorno, sizeof(exec_errorno));
+    while (r == -1 && errno == EINTR);
+
+    if (r == 0)
+      ; /* okay, EOF */
+    else if (r == sizeof(exec_errorno)) {
+      do
+        err = waitpid(*pid, &status, 0); /* okay, read errorno */
+      while (err == -1 && errno == EINTR);
+      assert(err == *pid);
+      err = exec_errorno;
+    } else if (r == -1 && errno == EPIPE) {
+      /* Something unknown happened to our child before spawn */
+      do
+        err = waitpid(*pid, &status, 0); /* okay, got EPIPE */
+      while (err == -1 && errno == EINTR);
+      assert(err == *pid);
+      err = UV_EPIPE;
+    } else
+      abort();
+  }
+
+  uv__close_nocheckstdio(signal_pipe[0]);
+
+  return err;
+}
+
+int uv_spawn(uv_loop_t* loop,
+             uv_process_t* process,
+             const uv_process_options_t* options) {
+#if defined(__APPLE__) && (TARGET_OS_TV || TARGET_OS_WATCH)
+  /* fork is marked __WATCHOS_PROHIBITED __TVOS_PROHIBITED. */
+  return UV_ENOSYS;
+#else
+  int pipes_storage[8][2];
+  int (*pipes)[2];
+  int stdio_count;
+  pid_t pid;
+  int err;
+  int exec_errorno;
+  int i;
+
+  assert(options->file != NULL);
+  assert(!(options->flags & ~(UV_PROCESS_DETACHED |
+                              UV_PROCESS_SETGID |
+                              UV_PROCESS_SETUID |
+                              UV_PROCESS_WINDOWS_HIDE |
+                              UV_PROCESS_WINDOWS_HIDE_CONSOLE |
+                              UV_PROCESS_WINDOWS_HIDE_GUI |
+                              UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS)));
+
+  uv__handle_init(loop, (uv_handle_t*)process, UV_PROCESS);
+  QUEUE_INIT(&process->queue);
+  process->status = 0;
+
+  stdio_count = options->stdio_count;
+  if (stdio_count < 3)
+    stdio_count = 3;
+
+  err = UV_ENOMEM;
+  pipes = pipes_storage;
+  if (stdio_count > (int) ARRAY_SIZE(pipes_storage))
+    pipes = (int (*)[2])uv__malloc(stdio_count * sizeof(*pipes));
+
+  if (pipes == NULL)
+    goto error;
+
+  for (i = 0; i < stdio_count; i++) {
+    pipes[i][0] = -1;
+    pipes[i][1] = -1;
+  }
+
+  for (i = 0; i < options->stdio_count; i++) {
+    err = uv__process_init_stdio(options->stdio + i, pipes[i]);
+    if (err)
+      goto error;
+  }
+
+#ifdef UV_USE_SIGCHLD
+  uv_signal_start(&loop->child_watcher, uv__chld, SIGCHLD);
+#endif
+
+  /* Spawn the child */
+  exec_errorno = uv__spawn_and_init_child(loop, options, stdio_count, pipes, &pid);
+
+#if 0
+  /* This runs into a nodejs issue (it expects initialized streams, even if the
+   * exec failed).
+   * See https://github.com/libuv/libuv/pull/3107#issuecomment-782482608 */
+  if (exec_errorno != 0)
+      goto error;
+#endif
+
+  /* Activate this handle if exec() happened successfully, even if we later
+   * fail to open a stdio handle. This ensures we can eventually reap the child
+   * with waitpid. */
+  if (exec_errorno == 0) {
+#ifndef UV_USE_SIGCHLD
+    struct kevent event;
+    EV_SET(&event, pid, EVFILT_PROC, EV_ADD | EV_ONESHOT, NOTE_EXIT, 0, 0);
+    if (kevent(loop->backend_fd, &event, 1, NULL, 0, NULL)) {
+      if (errno != ESRCH)
+        abort();
+      /* Process already exited. Call waitpid on the next loop iteration. */
+      process->flags |= UV_HANDLE_REAP;
+      loop->flags |= UV_LOOP_REAP_CHILDREN;
+    }
+#endif
+
+    process->pid = pid;
+    process->exit_cb = options->exit_cb;
+    QUEUE_INSERT_TAIL(&loop->process_handles, &process->queue);
+    uv__handle_start(process);
+  }
+
+  for (i = 0; i < options->stdio_count; i++) {
+    err = uv__process_open_stream(options->stdio + i, pipes[i]);
+    if (err == 0)
+      continue;
+
+    while (i--)
+      uv__process_close_stream(options->stdio + i);
+
+    goto error;
+  }
+
+  if (pipes != pipes_storage)
+    uv__free(pipes);
+
+  return exec_errorno;
+
+error:
+  if (pipes != NULL) {
+    for (i = 0; i < stdio_count; i++) {
+      if (i < options->stdio_count)
+        if (options->stdio[i].flags & (UV_INHERIT_FD | UV_INHERIT_STREAM))
+          continue;
+      if (pipes[i][0] != -1)
+        uv__close_nocheckstdio(pipes[i][0]);
+      if (pipes[i][1] != -1)
+        uv__close_nocheckstdio(pipes[i][1]);
+    }
+
+    if (pipes != pipes_storage)
+      uv__free(pipes);
+  }
+
+  return err;
+#endif
+}
+
+
+int uv_process_kill(uv_process_t* process, int signum) {
+  return uv_kill(process->pid, signum);
+}
+
+
+int uv_kill(int pid, int signum) {
+  if (kill(pid, signum)) {
+#if defined(__MVS__)
+    /* EPERM is returned if the process is a zombie. */
+    siginfo_t infop;
+    if (errno == EPERM &&
+        waitid(P_PID, pid, &infop, WNOHANG | WNOWAIT | WEXITED) == 0)
+      return 0;
+#endif
+    return UV__ERR(errno);
+  } else
+    return 0;
+}
+
+
+void uv__process_close(uv_process_t* handle) {
+  QUEUE_REMOVE(&handle->queue);
+  uv__handle_stop(handle);
+  if (QUEUE_EMPTY(&handle->loop->process_handles))
+    uv_signal_stop(&handle->loop->child_watcher);
+}
diff --git a/wpinet/src/main/native/thirdparty/libuv/src/unix/procfs-exepath.cpp b/wpinet/src/main/native/thirdparty/libuv/src/unix/procfs-exepath.cpp
new file mode 100644
index 0000000..00dc021
--- /dev/null
+++ b/wpinet/src/main/native/thirdparty/libuv/src/unix/procfs-exepath.cpp
@@ -0,0 +1,45 @@
+/* Copyright libuv project contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include "uv.h"
+#include "internal.h"
+
+#include <stddef.h>
+#include <unistd.h>
+
+int uv_exepath(char* buffer, size_t* size) {
+  ssize_t n;
+
+  if (buffer == NULL || size == NULL || *size == 0)
+    return UV_EINVAL;
+
+  n = *size - 1;
+  if (n > 0)
+    n = readlink("/proc/self/exe", buffer, n);
+
+  if (n == -1)
+    return UV__ERR(errno);
+
+  buffer[n] = '\0';
+  *size = n;
+
+  return 0;
+}
diff --git a/wpinet/src/main/native/thirdparty/libuv/src/unix/proctitle.cpp b/wpinet/src/main/native/thirdparty/libuv/src/unix/proctitle.cpp
new file mode 100644
index 0000000..8cdec75
--- /dev/null
+++ b/wpinet/src/main/native/thirdparty/libuv/src/unix/proctitle.cpp
@@ -0,0 +1,157 @@
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include "uv.h"
+#include "internal.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+struct uv__process_title {
+  char* str;
+  size_t len;  /* Length of the current process title. */
+  size_t cap;  /* Maximum capacity. Computed once in uv_setup_args(). */
+};
+
+extern void uv__set_process_title(const char* title);
+
+static uv_mutex_t process_title_mutex;
+static uv_once_t process_title_mutex_once = UV_ONCE_INIT;
+static struct uv__process_title process_title;
+static void* args_mem;
+
+
+static void init_process_title_mutex_once(void) {
+  uv_mutex_init(&process_title_mutex);
+}
+
+
+char** uv_setup_args(int argc, char** argv) {
+  struct uv__process_title pt;
+  char** new_argv;
+  size_t size;
+  char* s;
+  int i;
+
+  if (argc <= 0)
+    return argv;
+
+  pt.str = argv[0];
+  pt.len = strlen(argv[0]);
+  pt.cap = pt.len + 1;
+
+  /* Calculate how much memory we need for the argv strings. */
+  size = pt.cap;
+  for (i = 1; i < argc; i++)
+    size += strlen(argv[i]) + 1;
+
+  /* Add space for the argv pointers. */
+  size += (argc + 1) * sizeof(char*);
+
+  new_argv = (char**)uv__malloc(size);
+  if (new_argv == NULL)
+    return argv;
+
+  /* Copy over the strings and set up the pointer table. */
+  i = 0;
+  s = (char*) &new_argv[argc + 1];
+  size = pt.cap;
+  goto loop;
+
+  for (/* empty */; i < argc; i++) {
+    size = strlen(argv[i]) + 1;
+  loop:
+    memcpy(s, argv[i], size);
+    new_argv[i] = s;
+    s += size;
+  }
+  new_argv[i] = NULL;
+
+  pt.cap = argv[i - 1] + size - argv[0];
+
+  args_mem = new_argv;
+  process_title = pt;
+
+  return new_argv;
+}
+
+
+int uv_set_process_title(const char* title) {
+  struct uv__process_title* pt;
+  size_t len;
+
+  /* If uv_setup_args wasn't called or failed, we can't continue. */
+  if (args_mem == NULL)
+    return UV_ENOBUFS;
+
+  pt = &process_title;
+  len = strlen(title);
+
+  uv_once(&process_title_mutex_once, init_process_title_mutex_once);
+  uv_mutex_lock(&process_title_mutex);
+
+  if (len >= pt->cap) {
+    len = 0;
+    if (pt->cap > 0)
+      len = pt->cap - 1;
+  }
+
+  memcpy(pt->str, title, len);
+  memset(pt->str + len, '\0', pt->cap - len);
+  pt->len = len;
+  uv__set_process_title(pt->str);
+
+  uv_mutex_unlock(&process_title_mutex);
+
+  return 0;
+}
+
+
+int uv_get_process_title(char* buffer, size_t size) {
+  if (buffer == NULL || size == 0)
+    return UV_EINVAL;
+
+  /* If uv_setup_args wasn't called or failed, we can't continue. */
+  if (args_mem == NULL)
+    return UV_ENOBUFS;
+
+  uv_once(&process_title_mutex_once, init_process_title_mutex_once);
+  uv_mutex_lock(&process_title_mutex);
+
+  if (size <= process_title.len) {
+    uv_mutex_unlock(&process_title_mutex);
+    return UV_ENOBUFS;
+  }
+
+  if (process_title.len != 0)
+    memcpy(buffer, process_title.str, process_title.len + 1);
+
+  buffer[process_title.len] = '\0';
+
+  uv_mutex_unlock(&process_title_mutex);
+
+  return 0;
+}
+
+
+void uv__process_title_cleanup(void) {
+  uv__free(args_mem);  /* Keep valgrind happy. */
+  args_mem = NULL;
+}
diff --git a/wpinet/src/main/native/thirdparty/libuv/src/unix/pthread-fixes.cpp b/wpinet/src/main/native/thirdparty/libuv/src/unix/pthread-fixes.cpp
new file mode 100644
index 0000000..022d79c
--- /dev/null
+++ b/wpinet/src/main/native/thirdparty/libuv/src/unix/pthread-fixes.cpp
@@ -0,0 +1,58 @@
+/* Copyright (c) 2013, Sony Mobile Communications AB
+ * Copyright (c) 2012, Google Inc.
+   All rights reserved.
+
+   Redistribution and use in source and binary forms, with or without
+   modification, are permitted provided that the following conditions are
+   met:
+
+     * Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+       * Redistributions in binary form must reproduce the above
+   copyright notice, this list of conditions and the following disclaimer
+   in the documentation and/or other materials provided with the
+   distribution.
+       * Neither the name of Google Inc. nor the names of its
+   contributors may be used to endorse or promote products derived from
+   this software without specific prior written permission.
+
+   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/* Android versions < 4.1 have a broken pthread_sigmask. */
+#include "uv-common.h"
+
+#include <errno.h>
+#include <pthread.h>
+#include <signal.h>
+
+int uv__pthread_sigmask(int how, const sigset_t* set, sigset_t* oset) {
+  static int workaround;
+  int err;
+
+  if (uv__load_relaxed(&workaround)) {
+    return sigprocmask(how, set, oset);
+  } else {
+    err = pthread_sigmask(how, set, oset);
+    if (err) {
+      if (err == EINVAL && sigprocmask(how, set, oset) == 0) {
+        uv__store_relaxed(&workaround, 1);
+        return 0;
+      } else {
+        return -1;
+      }
+    }
+  }
+
+  return 0;
+}
diff --git a/wpinet/src/main/native/thirdparty/libuv/src/unix/random-devurandom.cpp b/wpinet/src/main/native/thirdparty/libuv/src/unix/random-devurandom.cpp
new file mode 100644
index 0000000..05e52a5
--- /dev/null
+++ b/wpinet/src/main/native/thirdparty/libuv/src/unix/random-devurandom.cpp
@@ -0,0 +1,93 @@
+/* Copyright libuv contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include "uv.h"
+#include "internal.h"
+
+#include <sys/stat.h>
+#include <unistd.h>
+
+static uv_once_t once = UV_ONCE_INIT;
+static int status;
+
+
+int uv__random_readpath(const char* path, void* buf, size_t buflen) {
+  struct stat s;
+  size_t pos;
+  ssize_t n;
+  int fd;
+
+  fd = uv__open_cloexec(path, O_RDONLY);
+
+  if (fd < 0)
+    return fd;
+
+  if (fstat(fd, &s)) {
+    uv__close(fd);
+    return UV__ERR(errno);
+  }
+
+  if (!S_ISCHR(s.st_mode)) {
+    uv__close(fd);
+    return UV_EIO;
+  }
+
+  for (pos = 0; pos != buflen; pos += n) {
+    do
+      n = read(fd, (char*) buf + pos, buflen - pos);
+    while (n == -1 && errno == EINTR);
+
+    if (n == -1) {
+      uv__close(fd);
+      return UV__ERR(errno);
+    }
+
+    if (n == 0) {
+      uv__close(fd);
+      return UV_EIO;
+    }
+  }
+
+  uv__close(fd);
+  return 0;
+}
+
+
+static void uv__random_devurandom_init(void) {
+  char c;
+
+  /* Linux's random(4) man page suggests applications should read at least
+   * once from /dev/random before switching to /dev/urandom in order to seed
+   * the system RNG. Reads from /dev/random can of course block indefinitely
+   * until entropy is available but that's the point.
+   */
+  status = uv__random_readpath("/dev/random", &c, 1);
+}
+
+
+int uv__random_devurandom(void* buf, size_t buflen) {
+  uv_once(&once, uv__random_devurandom_init);
+
+  if (status != 0)
+    return status;
+
+  return uv__random_readpath("/dev/urandom", buf, buflen);
+}
diff --git a/wpinet/src/main/native/thirdparty/libuv/src/unix/random-getentropy.cpp b/wpinet/src/main/native/thirdparty/libuv/src/unix/random-getentropy.cpp
new file mode 100644
index 0000000..c45d9fd
--- /dev/null
+++ b/wpinet/src/main/native/thirdparty/libuv/src/unix/random-getentropy.cpp
@@ -0,0 +1,57 @@
+/* Copyright libuv contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include "uv.h"
+#include "internal.h"
+
+#include <stddef.h>
+#include <dlfcn.h>
+
+typedef int (*uv__getentropy_cb)(void *, size_t);
+
+static uv__getentropy_cb uv__getentropy;
+static uv_once_t once = UV_ONCE_INIT;
+
+
+static void uv__random_getentropy_init(void) {
+  uv__getentropy = (uv__getentropy_cb) dlsym(RTLD_DEFAULT, "getentropy");
+}
+
+
+int uv__random_getentropy(void* buf, size_t buflen) {
+  size_t pos;
+  size_t stride;
+
+  uv_once(&once, uv__random_getentropy_init);
+
+  if (uv__getentropy == NULL)
+    return UV_ENOSYS;
+
+  /* getentropy() returns an error for requests > 256 bytes. */
+  for (pos = 0, stride = 256; pos + stride < buflen; pos += stride)
+    if (uv__getentropy((char *) buf + pos, stride))
+      return UV__ERR(errno);
+
+  if (uv__getentropy((char *) buf + pos, buflen - pos))
+    return UV__ERR(errno);
+
+  return 0;
+}
diff --git a/wpinet/src/main/native/thirdparty/libuv/src/unix/random-getrandom.cpp b/wpinet/src/main/native/thirdparty/libuv/src/unix/random-getrandom.cpp
new file mode 100644
index 0000000..bcc9408
--- /dev/null
+++ b/wpinet/src/main/native/thirdparty/libuv/src/unix/random-getrandom.cpp
@@ -0,0 +1,88 @@
+/* Copyright libuv contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include "uv.h"
+#include "internal.h"
+
+#ifdef __linux__
+
+#include "linux-syscalls.h"
+
+#define uv__random_getrandom_init() 0
+
+#else  /* !__linux__ */
+
+#include <stddef.h>
+#include <dlfcn.h>
+
+typedef ssize_t (*uv__getrandom_cb)(void *, size_t, unsigned);
+
+static uv__getrandom_cb uv__getrandom;
+static uv_once_t once = UV_ONCE_INIT;
+
+static void uv__random_getrandom_init_once(void) {
+  uv__getrandom = (uv__getrandom_cb) dlsym(RTLD_DEFAULT, "getrandom");
+}
+
+static int uv__random_getrandom_init(void) {
+  uv_once(&once, uv__random_getrandom_init_once);
+
+  if (uv__getrandom == NULL)
+    return UV_ENOSYS;
+
+  return 0;
+}
+
+#endif  /* !__linux__ */
+
+int uv__random_getrandom(void* buf, size_t buflen) {
+  ssize_t n;
+  size_t pos;
+  int rc;
+
+  rc = uv__random_getrandom_init();
+  if (rc != 0)
+    return rc;
+
+  for (pos = 0; pos != buflen; pos += n) {
+    do {
+      n = buflen - pos;
+
+      /* Most getrandom() implementations promise that reads <= 256 bytes
+       * will always succeed and won't be interrupted by signals.
+       * It's therefore useful to split it up in smaller reads because
+       * one big read may, in theory, continuously fail with EINTR.
+       */
+      if (n > 256)
+        n = 256;
+
+      n = uv__getrandom((char *) buf + pos, n, 0);
+    } while (n == -1 && errno == EINTR);
+
+    if (n == -1)
+      return UV__ERR(errno);
+
+    if (n == 0)
+      return UV_EIO;
+  }
+
+  return 0;
+}
diff --git a/wpinet/src/main/native/thirdparty/libuv/src/unix/random-sysctl-linux.cpp b/wpinet/src/main/native/thirdparty/libuv/src/unix/random-sysctl-linux.cpp
new file mode 100644
index 0000000..9ef18df
--- /dev/null
+++ b/wpinet/src/main/native/thirdparty/libuv/src/unix/random-sysctl-linux.cpp
@@ -0,0 +1,99 @@
+/* Copyright libuv contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include "uv.h"
+#include "internal.h"
+
+#include <errno.h>
+#include <string.h>
+
+#include <syscall.h>
+#include <unistd.h>
+
+
+struct uv__sysctl_args {
+  int* name;
+  int nlen;
+  void* oldval;
+  size_t* oldlenp;
+  void* newval;
+  size_t newlen;
+  unsigned long unused[4];
+};
+
+
+int uv__random_sysctl(void* buf, size_t buflen) {
+  static int name[] = {1 /*CTL_KERN*/, 40 /*KERN_RANDOM*/, 6 /*RANDOM_UUID*/};
+  struct uv__sysctl_args args;
+  char uuid[16];
+  char* p;
+  char* pe;
+  size_t n;
+
+  p = (char*)buf;
+  pe = p + buflen;
+
+  while (p < pe) {
+    memset(&args, 0, sizeof(args));
+
+    args.name = name;
+    args.nlen = ARRAY_SIZE(name);
+    args.oldval = uuid;
+    args.oldlenp = &n;
+    n = sizeof(uuid);
+
+    /* Emits a deprecation warning with some kernels but that seems like
+     * an okay trade-off for the fallback of the fallback: this function is
+     * only called when neither getrandom(2) nor /dev/urandom are available.
+     * Fails with ENOSYS on kernels configured without CONFIG_SYSCTL_SYSCALL.
+     * At least arm64 never had a _sysctl system call and therefore doesn't
+     * have a SYS__sysctl define either.
+     */
+#ifdef SYS__sysctl
+    if (syscall(SYS__sysctl, &args) == -1)
+      return UV__ERR(errno);
+#else
+    {
+      (void) &args;
+      return UV_ENOSYS;
+    }
+#endif
+
+    if (n != sizeof(uuid))
+      return UV_EIO;  /* Can't happen. */
+
+    /* uuid[] is now a type 4 UUID. Bytes 6 and 8 (counting from zero) contain
+     * 4 and 5 bits of entropy, respectively. For ease of use, we skip those
+     * and only use 14 of the 16 bytes.
+     */
+    uuid[6] = uuid[14];
+    uuid[8] = uuid[15];
+
+    n = pe - p;
+    if (n > 14)
+      n = 14;
+
+    memcpy(p, uuid, n);
+    p += n;
+  }
+
+  return 0;
+}
diff --git a/wpinet/src/main/native/thirdparty/libuv/src/unix/signal.cpp b/wpinet/src/main/native/thirdparty/libuv/src/unix/signal.cpp
new file mode 100644
index 0000000..1133c73
--- /dev/null
+++ b/wpinet/src/main/native/thirdparty/libuv/src/unix/signal.cpp
@@ -0,0 +1,558 @@
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include "uv.h"
+#include "internal.h"
+
+#include <assert.h>
+#include <errno.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#ifndef SA_RESTART
+# define SA_RESTART 0
+#endif
+
+typedef struct {
+  uv_signal_t* handle;
+  int signum;
+} uv__signal_msg_t;
+
+RB_HEAD(uv__signal_tree_s, uv_signal_s);
+
+
+static int uv__signal_unlock(void);
+static int uv__signal_start(uv_signal_t* handle,
+                            uv_signal_cb signal_cb,
+                            int signum,
+                            int oneshot);
+static void uv__signal_event(uv_loop_t* loop, uv__io_t* w, unsigned int events);
+static int uv__signal_compare(uv_signal_t* w1, uv_signal_t* w2);
+static void uv__signal_stop(uv_signal_t* handle);
+static void uv__signal_unregister_handler(int signum);
+
+
+static uv_once_t uv__signal_global_init_guard = UV_ONCE_INIT;
+static struct uv__signal_tree_s uv__signal_tree =
+    RB_INITIALIZER(uv__signal_tree);
+static int uv__signal_lock_pipefd[2] = { -1, -1 };
+
+RB_GENERATE_STATIC(uv__signal_tree_s,
+                   uv_signal_s, tree_entry,
+                   uv__signal_compare)
+
+static void uv__signal_global_reinit(void);
+
+static void uv__signal_global_init(void) {
+  if (uv__signal_lock_pipefd[0] == -1)
+    /* pthread_atfork can register before and after handlers, one
+     * for each child. This only registers one for the child. That
+     * state is both persistent and cumulative, so if we keep doing
+     * it the handler functions will be called multiple times. Thus
+     * we only want to do it once.
+     */
+    if (pthread_atfork(NULL, NULL, &uv__signal_global_reinit))
+      abort();
+
+  uv__signal_global_reinit();
+}
+
+
+void uv__signal_cleanup(void) {
+  /* We can only use signal-safe functions here.
+   * That includes read/write and close, fortunately.
+   * We do all of this directly here instead of resetting
+   * uv__signal_global_init_guard because
+   * uv__signal_global_once_init is only called from uv_loop_init
+   * and this needs to function in existing loops.
+   */
+  if (uv__signal_lock_pipefd[0] != -1) {
+    uv__close(uv__signal_lock_pipefd[0]);
+    uv__signal_lock_pipefd[0] = -1;
+  }
+
+  if (uv__signal_lock_pipefd[1] != -1) {
+    uv__close(uv__signal_lock_pipefd[1]);
+    uv__signal_lock_pipefd[1] = -1;
+  }
+}
+
+
+static void uv__signal_global_reinit(void) {
+  uv__signal_cleanup();
+
+  if (uv__make_pipe(uv__signal_lock_pipefd, 0))
+    abort();
+
+  if (uv__signal_unlock())
+    abort();
+}
+
+
+void uv__signal_global_once_init(void) {
+  uv_once(&uv__signal_global_init_guard, uv__signal_global_init);
+}
+
+
+static int uv__signal_lock(void) {
+  int r;
+  char data;
+
+  do {
+    r = read(uv__signal_lock_pipefd[0], &data, sizeof data);
+  } while (r < 0 && errno == EINTR);
+
+  return (r < 0) ? -1 : 0;
+}
+
+
+static int uv__signal_unlock(void) {
+  int r;
+  char data = 42;
+
+  do {
+    r = write(uv__signal_lock_pipefd[1], &data, sizeof data);
+  } while (r < 0 && errno == EINTR);
+
+  return (r < 0) ? -1 : 0;
+}
+
+
+static void uv__signal_block_and_lock(sigset_t* saved_sigmask) {
+  sigset_t new_mask;
+
+  if (sigfillset(&new_mask))
+    abort();
+
+  /* to shut up valgrind */
+  sigemptyset(saved_sigmask);
+  if (pthread_sigmask(SIG_SETMASK, &new_mask, saved_sigmask))
+    abort();
+
+  if (uv__signal_lock())
+    abort();
+}
+
+
+static void uv__signal_unlock_and_unblock(sigset_t* saved_sigmask) {
+  if (uv__signal_unlock())
+    abort();
+
+  if (pthread_sigmask(SIG_SETMASK, saved_sigmask, NULL))
+    abort();
+}
+
+
+static uv_signal_t* uv__signal_first_handle(int signum) {
+  /* This function must be called with the signal lock held. */
+  uv_signal_t lookup;
+  uv_signal_t* handle;
+
+  lookup.signum = signum;
+  lookup.flags = 0;
+  lookup.loop = NULL;
+
+  handle = RB_NFIND(uv__signal_tree_s, &uv__signal_tree, &lookup);
+
+  if (handle != NULL && handle->signum == signum)
+    return handle;
+
+  return NULL;
+}
+
+
+static void uv__signal_handler(int signum) {
+  uv__signal_msg_t msg;
+  uv_signal_t* handle;
+  int saved_errno;
+
+  saved_errno = errno;
+  memset(&msg, 0, sizeof msg);
+
+  if (uv__signal_lock()) {
+    errno = saved_errno;
+    return;
+  }
+
+  for (handle = uv__signal_first_handle(signum);
+       handle != NULL && handle->signum == signum;
+       handle = RB_NEXT(uv__signal_tree_s, &uv__signal_tree, handle)) {
+    int r;
+
+    msg.signum = signum;
+    msg.handle = handle;
+
+    /* write() should be atomic for small data chunks, so the entire message
+     * should be written at once. In theory the pipe could become full, in
+     * which case the user is out of luck.
+     */
+    do {
+      r = write(handle->loop->signal_pipefd[1], &msg, sizeof msg);
+    } while (r == -1 && errno == EINTR);
+
+    assert(r == sizeof msg ||
+           (r == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)));
+
+    if (r != -1)
+      handle->caught_signals++;
+  }
+
+  uv__signal_unlock();
+  errno = saved_errno;
+}
+
+
+static int uv__signal_register_handler(int signum, int oneshot) {
+  /* When this function is called, the signal lock must be held. */
+  struct sigaction sa;
+
+  /* XXX use a separate signal stack? */
+  memset(&sa, 0, sizeof(sa));
+  if (sigfillset(&sa.sa_mask))
+    abort();
+  sa.sa_handler = uv__signal_handler;
+  sa.sa_flags = SA_RESTART;
+  if (oneshot)
+    sa.sa_flags |= SA_RESETHAND;
+
+  /* XXX save old action so we can restore it later on? */
+  if (sigaction(signum, &sa, NULL))
+    return UV__ERR(errno);
+
+  return 0;
+}
+
+
+static void uv__signal_unregister_handler(int signum) {
+  /* When this function is called, the signal lock must be held. */
+  struct sigaction sa;
+
+  memset(&sa, 0, sizeof(sa));
+  sa.sa_handler = SIG_DFL;
+
+  /* sigaction can only fail with EINVAL or EFAULT; an attempt to deregister a
+   * signal implies that it was successfully registered earlier, so EINVAL
+   * should never happen.
+   */
+  if (sigaction(signum, &sa, NULL))
+    abort();
+}
+
+
+static int uv__signal_loop_once_init(uv_loop_t* loop) {
+  int err;
+
+  /* Return if already initialized. */
+  if (loop->signal_pipefd[0] != -1)
+    return 0;
+
+  err = uv__make_pipe(loop->signal_pipefd, UV_NONBLOCK_PIPE);
+  if (err)
+    return err;
+
+  uv__io_init(&loop->signal_io_watcher,
+              uv__signal_event,
+              loop->signal_pipefd[0]);
+  uv__io_start(loop, &loop->signal_io_watcher, POLLIN);
+
+  return 0;
+}
+
+
+int uv__signal_loop_fork(uv_loop_t* loop) {
+  uv__io_stop(loop, &loop->signal_io_watcher, POLLIN);
+  uv__close(loop->signal_pipefd[0]);
+  uv__close(loop->signal_pipefd[1]);
+  loop->signal_pipefd[0] = -1;
+  loop->signal_pipefd[1] = -1;
+  return uv__signal_loop_once_init(loop);
+}
+
+
+void uv__signal_loop_cleanup(uv_loop_t* loop) {
+  QUEUE* q;
+
+  /* Stop all the signal watchers that are still attached to this loop. This
+   * ensures that the (shared) signal tree doesn't contain any invalid entries
+   * entries, and that signal handlers are removed when appropriate.
+   * It's safe to use QUEUE_FOREACH here because the handles and the handle
+   * queue are not modified by uv__signal_stop().
+   */
+  QUEUE_FOREACH(q, &loop->handle_queue) {
+    uv_handle_t* handle = QUEUE_DATA(q, uv_handle_t, handle_queue);
+
+    if (handle->type == UV_SIGNAL)
+      uv__signal_stop((uv_signal_t*) handle);
+  }
+
+  if (loop->signal_pipefd[0] != -1) {
+    uv__close(loop->signal_pipefd[0]);
+    loop->signal_pipefd[0] = -1;
+  }
+
+  if (loop->signal_pipefd[1] != -1) {
+    uv__close(loop->signal_pipefd[1]);
+    loop->signal_pipefd[1] = -1;
+  }
+}
+
+
+int uv_signal_init(uv_loop_t* loop, uv_signal_t* handle) {
+  int err;
+
+  err = uv__signal_loop_once_init(loop);
+  if (err)
+    return err;
+
+  uv__handle_init(loop, (uv_handle_t*) handle, UV_SIGNAL);
+  handle->signum = 0;
+  handle->caught_signals = 0;
+  handle->dispatched_signals = 0;
+
+  return 0;
+}
+
+
+void uv__signal_close(uv_signal_t* handle) {
+  uv__signal_stop(handle);
+}
+
+
+int uv_signal_start(uv_signal_t* handle, uv_signal_cb signal_cb, int signum) {
+  return uv__signal_start(handle, signal_cb, signum, 0);
+}
+
+
+int uv_signal_start_oneshot(uv_signal_t* handle,
+                            uv_signal_cb signal_cb,
+                            int signum) {
+  return uv__signal_start(handle, signal_cb, signum, 1);
+}
+
+
+static int uv__signal_start(uv_signal_t* handle,
+                            uv_signal_cb signal_cb,
+                            int signum,
+                            int oneshot) {
+  sigset_t saved_sigmask;
+  int err;
+  uv_signal_t* first_handle;
+
+  assert(!uv__is_closing(handle));
+
+  /* If the user supplies signum == 0, then return an error already. If the
+   * signum is otherwise invalid then uv__signal_register will find out
+   * eventually.
+   */
+  if (signum == 0)
+    return UV_EINVAL;
+
+  /* Short circuit: if the signal watcher is already watching {signum} don't
+   * go through the process of deregistering and registering the handler.
+   * Additionally, this avoids pending signals getting lost in the small
+   * time frame that handle->signum == 0.
+   */
+  if (signum == handle->signum) {
+    handle->signal_cb = signal_cb;
+    return 0;
+  }
+
+  /* If the signal handler was already active, stop it first. */
+  if (handle->signum != 0) {
+    uv__signal_stop(handle);
+  }
+
+  uv__signal_block_and_lock(&saved_sigmask);
+
+  /* If at this point there are no active signal watchers for this signum (in
+   * any of the loops), it's time to try and register a handler for it here.
+   * Also in case there's only one-shot handlers and a regular handler comes in.
+   */
+  first_handle = uv__signal_first_handle(signum);
+  if (first_handle == NULL ||
+      (!oneshot && (first_handle->flags & UV_SIGNAL_ONE_SHOT))) {
+    err = uv__signal_register_handler(signum, oneshot);
+    if (err) {
+      /* Registering the signal handler failed. Must be an invalid signal. */
+      uv__signal_unlock_and_unblock(&saved_sigmask);
+      return err;
+    }
+  }
+
+  handle->signum = signum;
+  if (oneshot)
+    handle->flags |= UV_SIGNAL_ONE_SHOT;
+
+  RB_INSERT(uv__signal_tree_s, &uv__signal_tree, handle);
+
+  uv__signal_unlock_and_unblock(&saved_sigmask);
+
+  handle->signal_cb = signal_cb;
+  uv__handle_start(handle);
+
+  return 0;
+}
+
+
+static void uv__signal_event(uv_loop_t* loop,
+                             uv__io_t* w,
+                             unsigned int events) {
+  uv__signal_msg_t* msg;
+  uv_signal_t* handle;
+  char buf[sizeof(uv__signal_msg_t) * 32];
+  size_t bytes, end, i;
+  int r;
+
+  bytes = 0;
+  end = 0;
+
+  do {
+    r = read(loop->signal_pipefd[0], buf + bytes, sizeof(buf) - bytes);
+
+    if (r == -1 && errno == EINTR)
+      continue;
+
+    if (r == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)) {
+      /* If there are bytes in the buffer already (which really is extremely
+       * unlikely if possible at all) we can't exit the function here. We'll
+       * spin until more bytes are read instead.
+       */
+      if (bytes > 0)
+        continue;
+
+      /* Otherwise, there was nothing there. */
+      return;
+    }
+
+    /* Other errors really should never happen. */
+    if (r == -1)
+      abort();
+
+    bytes += r;
+
+    /* `end` is rounded down to a multiple of sizeof(uv__signal_msg_t). */
+    end = (bytes / sizeof(uv__signal_msg_t)) * sizeof(uv__signal_msg_t);
+
+    for (i = 0; i < end; i += sizeof(uv__signal_msg_t)) {
+      msg = (uv__signal_msg_t*) (buf + i);
+      handle = msg->handle;
+
+      if (msg->signum == handle->signum) {
+        assert(!(handle->flags & UV_HANDLE_CLOSING));
+        handle->signal_cb(handle, handle->signum);
+      }
+
+      handle->dispatched_signals++;
+
+      if (handle->flags & UV_SIGNAL_ONE_SHOT)
+        uv__signal_stop(handle);
+    }
+
+    bytes -= end;
+
+    /* If there are any "partial" messages left, move them to the start of the
+     * the buffer, and spin. This should not happen.
+     */
+    if (bytes) {
+      memmove(buf, buf + end, bytes);
+      continue;
+    }
+  } while (end == sizeof buf);
+}
+
+
+static int uv__signal_compare(uv_signal_t* w1, uv_signal_t* w2) {
+  int f1;
+  int f2;
+  /* Compare signums first so all watchers with the same signnum end up
+   * adjacent.
+   */
+  if (w1->signum < w2->signum) return -1;
+  if (w1->signum > w2->signum) return 1;
+
+  /* Handlers without UV_SIGNAL_ONE_SHOT set will come first, so if the first
+   * handler returned is a one-shot handler, the rest will be too.
+   */
+  f1 = w1->flags & UV_SIGNAL_ONE_SHOT;
+  f2 = w2->flags & UV_SIGNAL_ONE_SHOT;
+  if (f1 < f2) return -1;
+  if (f1 > f2) return 1;
+
+  /* Sort by loop pointer, so we can easily look up the first item after
+   * { .signum = x, .loop = NULL }.
+   */
+  if (w1->loop < w2->loop) return -1;
+  if (w1->loop > w2->loop) return 1;
+
+  if (w1 < w2) return -1;
+  if (w1 > w2) return 1;
+
+  return 0;
+}
+
+
+int uv_signal_stop(uv_signal_t* handle) {
+  assert(!uv__is_closing(handle));
+  uv__signal_stop(handle);
+  return 0;
+}
+
+
+static void uv__signal_stop(uv_signal_t* handle) {
+  uv_signal_t* removed_handle;
+  sigset_t saved_sigmask;
+  uv_signal_t* first_handle;
+  int rem_oneshot;
+  int first_oneshot;
+  int ret;
+
+  /* If the watcher wasn't started, this is a no-op. */
+  if (handle->signum == 0)
+    return;
+
+  uv__signal_block_and_lock(&saved_sigmask);
+
+  removed_handle = RB_REMOVE(uv__signal_tree_s, &uv__signal_tree, handle);
+  assert(removed_handle == handle);
+  (void) removed_handle;
+
+  /* Check if there are other active signal watchers observing this signal. If
+   * not, unregister the signal handler.
+   */
+  first_handle = uv__signal_first_handle(handle->signum);
+  if (first_handle == NULL) {
+    uv__signal_unregister_handler(handle->signum);
+  } else {
+    rem_oneshot = handle->flags & UV_SIGNAL_ONE_SHOT;
+    first_oneshot = first_handle->flags & UV_SIGNAL_ONE_SHOT;
+    if (first_oneshot && !rem_oneshot) {
+      ret = uv__signal_register_handler(handle->signum, 1);
+      assert(ret == 0);
+      (void)ret;
+    }
+  }
+
+  uv__signal_unlock_and_unblock(&saved_sigmask);
+
+  handle->signum = 0;
+  uv__handle_stop(handle);
+}
diff --git a/wpinet/src/main/native/thirdparty/libuv/src/unix/spinlock.h b/wpinet/src/main/native/thirdparty/libuv/src/unix/spinlock.h
new file mode 100644
index 0000000..a20c83c
--- /dev/null
+++ b/wpinet/src/main/native/thirdparty/libuv/src/unix/spinlock.h
@@ -0,0 +1,53 @@
+/* Copyright (c) 2013, Ben Noordhuis <info@bnoordhuis.nl>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef UV_SPINLOCK_H_
+#define UV_SPINLOCK_H_
+
+#include "internal.h"  /* ACCESS_ONCE, UV_UNUSED */
+#include "atomic-ops.h"
+
+#define UV_SPINLOCK_INITIALIZER { 0 }
+
+typedef struct {
+  int lock;
+} uv_spinlock_t;
+
+UV_UNUSED(static void uv_spinlock_init(uv_spinlock_t* spinlock));
+UV_UNUSED(static void uv_spinlock_lock(uv_spinlock_t* spinlock));
+UV_UNUSED(static void uv_spinlock_unlock(uv_spinlock_t* spinlock));
+UV_UNUSED(static int uv_spinlock_trylock(uv_spinlock_t* spinlock));
+
+UV_UNUSED(static void uv_spinlock_init(uv_spinlock_t* spinlock)) {
+  ACCESS_ONCE(int, spinlock->lock) = 0;
+}
+
+UV_UNUSED(static void uv_spinlock_lock(uv_spinlock_t* spinlock)) {
+  while (!uv_spinlock_trylock(spinlock)) cpu_relax();
+}
+
+UV_UNUSED(static void uv_spinlock_unlock(uv_spinlock_t* spinlock)) {
+  ACCESS_ONCE(int, spinlock->lock) = 0;
+}
+
+UV_UNUSED(static int uv_spinlock_trylock(uv_spinlock_t* spinlock)) {
+  /* TODO(bnoordhuis) Maybe change to a ticket lock to guarantee fair queueing.
+   * Not really critical until we have locks that are (frequently) contended
+   * for by several threads.
+   */
+  return 0 == cmpxchgi(&spinlock->lock, 0, 1);
+}
+
+#endif  /* UV_SPINLOCK_H_ */
diff --git a/wpinet/src/main/native/thirdparty/libuv/src/unix/stream.cpp b/wpinet/src/main/native/thirdparty/libuv/src/unix/stream.cpp
new file mode 100644
index 0000000..fa25812
--- /dev/null
+++ b/wpinet/src/main/native/thirdparty/libuv/src/unix/stream.cpp
@@ -0,0 +1,1639 @@
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include "uv.h"
+#include "internal.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <errno.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+#include <sys/un.h>
+#include <unistd.h>
+#include <limits.h> /* IOV_MAX */
+
+#if defined(__APPLE__)
+# include <sys/event.h>
+# include <sys/time.h>
+# include <sys/select.h>
+
+/* Forward declaration */
+typedef struct uv__stream_select_s uv__stream_select_t;
+
+struct uv__stream_select_s {
+  uv_stream_t* stream;
+  uv_thread_t thread;
+  uv_sem_t close_sem;
+  uv_sem_t async_sem;
+  uv_async_t async;
+  int events;
+  int fake_fd;
+  int int_fd;
+  int fd;
+  fd_set* sread;
+  size_t sread_sz;
+  fd_set* swrite;
+  size_t swrite_sz;
+};
+#endif /* defined(__APPLE__) */
+
+static void uv__stream_connect(uv_stream_t*);
+static void uv__write(uv_stream_t* stream);
+static void uv__read(uv_stream_t* stream);
+static void uv__stream_io(uv_loop_t* loop, uv__io_t* w, unsigned int events);
+static void uv__write_callbacks(uv_stream_t* stream);
+static size_t uv__write_req_size(uv_write_t* req);
+static void uv__drain(uv_stream_t* stream);
+
+
+void uv__stream_init(uv_loop_t* loop,
+                     uv_stream_t* stream,
+                     uv_handle_type type) {
+  int err;
+
+  uv__handle_init(loop, (uv_handle_t*)stream, type);
+  stream->read_cb = NULL;
+  stream->alloc_cb = NULL;
+  stream->close_cb = NULL;
+  stream->connection_cb = NULL;
+  stream->connect_req = NULL;
+  stream->shutdown_req = NULL;
+  stream->accepted_fd = -1;
+  stream->queued_fds = NULL;
+  stream->delayed_error = 0;
+  QUEUE_INIT(&stream->write_queue);
+  QUEUE_INIT(&stream->write_completed_queue);
+  stream->write_queue_size = 0;
+
+  if (loop->emfile_fd == -1) {
+    err = uv__open_cloexec("/dev/null", O_RDONLY);
+    if (err < 0)
+        /* In the rare case that "/dev/null" isn't mounted open "/"
+         * instead.
+         */
+        err = uv__open_cloexec("/", O_RDONLY);
+    if (err >= 0)
+      loop->emfile_fd = err;
+  }
+
+#if defined(__APPLE__)
+  stream->select = NULL;
+#endif /* defined(__APPLE_) */
+
+  uv__io_init(&stream->io_watcher, uv__stream_io, -1);
+}
+
+
+static void uv__stream_osx_interrupt_select(uv_stream_t* stream) {
+#if defined(__APPLE__)
+  /* Notify select() thread about state change */
+  uv__stream_select_t* s;
+  int r;
+
+  s = (uv__stream_select_t*)stream->select;
+  if (s == NULL)
+    return;
+
+  /* Interrupt select() loop
+   * NOTE: fake_fd and int_fd are socketpair(), thus writing to one will
+   * emit read event on other side
+   */
+  do
+    r = write(s->fake_fd, "x", 1);
+  while (r == -1 && errno == EINTR);
+
+  assert(r == 1);
+#else  /* !defined(__APPLE__) */
+  /* No-op on any other platform */
+#endif  /* !defined(__APPLE__) */
+}
+
+
+#if defined(__APPLE__)
+static void uv__stream_osx_select(void* arg) {
+  uv_stream_t* stream;
+  uv__stream_select_t* s;
+  char buf[1024];
+  int events;
+  int fd;
+  int r;
+  int max_fd;
+
+  stream = (uv_stream_t*)arg;
+  s = (uv__stream_select_t*)stream->select;
+  fd = s->fd;
+
+  if (fd > s->int_fd)
+    max_fd = fd;
+  else
+    max_fd = s->int_fd;
+
+  for (;;) {
+    /* Terminate on semaphore */
+    if (uv_sem_trywait(&s->close_sem) == 0)
+      break;
+
+    /* Watch fd using select(2) */
+    memset(s->sread, 0, s->sread_sz);
+    memset(s->swrite, 0, s->swrite_sz);
+
+    if (uv__io_active(&stream->io_watcher, POLLIN))
+      FD_SET(fd, s->sread);
+    if (uv__io_active(&stream->io_watcher, POLLOUT))
+      FD_SET(fd, s->swrite);
+    FD_SET(s->int_fd, s->sread);
+
+    /* Wait indefinitely for fd events */
+    r = select(max_fd + 1, s->sread, s->swrite, NULL, NULL);
+    if (r == -1) {
+      if (errno == EINTR)
+        continue;
+
+      /* XXX: Possible?! */
+      abort();
+    }
+
+    /* Ignore timeouts */
+    if (r == 0)
+      continue;
+
+    /* Empty socketpair's buffer in case of interruption */
+    if (FD_ISSET(s->int_fd, s->sread))
+      for (;;) {
+        r = read(s->int_fd, buf, sizeof(buf));
+
+        if (r == sizeof(buf))
+          continue;
+
+        if (r != -1)
+          break;
+
+        if (errno == EAGAIN || errno == EWOULDBLOCK)
+          break;
+
+        if (errno == EINTR)
+          continue;
+
+        abort();
+      }
+
+    /* Handle events */
+    events = 0;
+    if (FD_ISSET(fd, s->sread))
+      events |= POLLIN;
+    if (FD_ISSET(fd, s->swrite))
+      events |= POLLOUT;
+
+    assert(events != 0 || FD_ISSET(s->int_fd, s->sread));
+    if (events != 0) {
+      ACCESS_ONCE(int, s->events) = events;
+
+      uv_async_send(&s->async);
+      uv_sem_wait(&s->async_sem);
+
+      /* Should be processed at this stage */
+      assert((s->events == 0) || (stream->flags & UV_HANDLE_CLOSING));
+    }
+  }
+}
+
+
+static void uv__stream_osx_select_cb(uv_async_t* handle) {
+  uv__stream_select_t* s;
+  uv_stream_t* stream;
+  int events;
+
+  s = container_of(handle, uv__stream_select_t, async);
+  stream = s->stream;
+
+  /* Get and reset stream's events */
+  events = s->events;
+  ACCESS_ONCE(int, s->events) = 0;
+
+  assert(events != 0);
+  assert(events == (events & (POLLIN | POLLOUT)));
+
+  /* Invoke callback on event-loop */
+  if ((events & POLLIN) && uv__io_active(&stream->io_watcher, POLLIN))
+    uv__stream_io(stream->loop, &stream->io_watcher, POLLIN);
+
+  if ((events & POLLOUT) && uv__io_active(&stream->io_watcher, POLLOUT))
+    uv__stream_io(stream->loop, &stream->io_watcher, POLLOUT);
+
+  if (stream->flags & UV_HANDLE_CLOSING)
+    return;
+
+  /* NOTE: It is important to do it here, otherwise `select()` might be called
+   * before the actual `uv__read()`, leading to the blocking syscall
+   */
+  uv_sem_post(&s->async_sem);
+}
+
+
+static void uv__stream_osx_cb_close(uv_handle_t* async) {
+  uv__stream_select_t* s;
+
+  s = container_of(async, uv__stream_select_t, async);
+  uv__free(s);
+}
+
+
+int uv__stream_try_select(uv_stream_t* stream, int* fd) {
+  /*
+   * kqueue doesn't work with some files from /dev mount on osx.
+   * select(2) in separate thread for those fds
+   */
+
+  struct kevent filter[1];
+  struct kevent events[1];
+  struct timespec timeout;
+  uv__stream_select_t* s;
+  int fds[2];
+  int err;
+  int ret;
+  int kq;
+  int old_fd;
+  int max_fd;
+  size_t sread_sz;
+  size_t swrite_sz;
+
+  kq = kqueue();
+  if (kq == -1) {
+    perror("(libuv) kqueue()");
+    return UV__ERR(errno);
+  }
+
+  EV_SET(&filter[0], *fd, EVFILT_READ, EV_ADD | EV_ENABLE, 0, 0, 0);
+
+  /* Use small timeout, because we only want to capture EINVALs */
+  timeout.tv_sec = 0;
+  timeout.tv_nsec = 1;
+
+  do
+    ret = kevent(kq, filter, 1, events, 1, &timeout);
+  while (ret == -1 && errno == EINTR);
+
+  uv__close(kq);
+
+  if (ret == -1)
+    return UV__ERR(errno);
+
+  if (ret == 0 || (events[0].flags & EV_ERROR) == 0 || events[0].data != EINVAL)
+    return 0;
+
+  /* At this point we definitely know that this fd won't work with kqueue */
+
+  /*
+   * Create fds for io watcher and to interrupt the select() loop.
+   * NOTE: do it ahead of malloc below to allocate enough space for fd_sets
+   */
+  if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds))
+    return UV__ERR(errno);
+
+  max_fd = *fd;
+  if (fds[1] > max_fd)
+    max_fd = fds[1];
+
+  sread_sz = ROUND_UP(max_fd + 1, sizeof(uint32_t) * NBBY) / NBBY;
+  swrite_sz = sread_sz;
+
+  s = (uv__stream_select_t*)uv__malloc(sizeof(*s) + sread_sz + swrite_sz);
+  if (s == NULL) {
+    err = UV_ENOMEM;
+    goto failed_malloc;
+  }
+
+  s->events = 0;
+  s->fd = *fd;
+  s->sread = (fd_set*) ((char*) s + sizeof(*s));
+  s->sread_sz = sread_sz;
+  s->swrite = (fd_set*) ((char*) s->sread + sread_sz);
+  s->swrite_sz = swrite_sz;
+
+  err = uv_async_init(stream->loop, &s->async, uv__stream_osx_select_cb);
+  if (err)
+    goto failed_async_init;
+
+  s->async.flags |= UV_HANDLE_INTERNAL;
+  uv__handle_unref(&s->async);
+
+  err = uv_sem_init(&s->close_sem, 0);
+  if (err != 0)
+    goto failed_close_sem_init;
+
+  err = uv_sem_init(&s->async_sem, 0);
+  if (err != 0)
+    goto failed_async_sem_init;
+
+  s->fake_fd = fds[0];
+  s->int_fd = fds[1];
+
+  old_fd = *fd;
+  s->stream = stream;
+  stream->select = s;
+  *fd = s->fake_fd;
+
+  err = uv_thread_create(&s->thread, uv__stream_osx_select, stream);
+  if (err != 0)
+    goto failed_thread_create;
+
+  return 0;
+
+failed_thread_create:
+  s->stream = NULL;
+  stream->select = NULL;
+  *fd = old_fd;
+
+  uv_sem_destroy(&s->async_sem);
+
+failed_async_sem_init:
+  uv_sem_destroy(&s->close_sem);
+
+failed_close_sem_init:
+  uv__close(fds[0]);
+  uv__close(fds[1]);
+  uv_close((uv_handle_t*) &s->async, uv__stream_osx_cb_close);
+  return err;
+
+failed_async_init:
+  uv__free(s);
+
+failed_malloc:
+  uv__close(fds[0]);
+  uv__close(fds[1]);
+
+  return err;
+}
+#endif /* defined(__APPLE__) */
+
+
+int uv__stream_open(uv_stream_t* stream, int fd, int flags) {
+#if defined(__APPLE__)
+  int enable;
+#endif
+
+  if (!(stream->io_watcher.fd == -1 || stream->io_watcher.fd == fd))
+    return UV_EBUSY;
+
+  assert(fd >= 0);
+  stream->flags |= flags;
+
+  if (stream->type == UV_TCP) {
+    if ((stream->flags & UV_HANDLE_TCP_NODELAY) && uv__tcp_nodelay(fd, 1))
+      return UV__ERR(errno);
+
+    /* TODO Use delay the user passed in. */
+    if ((stream->flags & UV_HANDLE_TCP_KEEPALIVE) &&
+        uv__tcp_keepalive(fd, 1, 60)) {
+      return UV__ERR(errno);
+    }
+  }
+
+#if defined(__APPLE__)
+  enable = 1;
+  if (setsockopt(fd, SOL_SOCKET, SO_OOBINLINE, &enable, sizeof(enable)) &&
+      errno != ENOTSOCK &&
+      errno != EINVAL) {
+    return UV__ERR(errno);
+  }
+#endif
+
+  stream->io_watcher.fd = fd;
+
+  return 0;
+}
+
+
+void uv__stream_flush_write_queue(uv_stream_t* stream, int error) {
+  uv_write_t* req;
+  QUEUE* q;
+  while (!QUEUE_EMPTY(&stream->write_queue)) {
+    q = QUEUE_HEAD(&stream->write_queue);
+    QUEUE_REMOVE(q);
+
+    req = QUEUE_DATA(q, uv_write_t, queue);
+    req->error = error;
+
+    QUEUE_INSERT_TAIL(&stream->write_completed_queue, &req->queue);
+  }
+}
+
+
+void uv__stream_destroy(uv_stream_t* stream) {
+  assert(!uv__io_active(&stream->io_watcher, POLLIN | POLLOUT));
+  assert(stream->flags & UV_HANDLE_CLOSED);
+
+  if (stream->connect_req) {
+    uv__req_unregister(stream->loop, stream->connect_req);
+    stream->connect_req->cb(stream->connect_req, UV_ECANCELED);
+    stream->connect_req = NULL;
+  }
+
+  uv__stream_flush_write_queue(stream, UV_ECANCELED);
+  uv__write_callbacks(stream);
+  uv__drain(stream);
+
+  assert(stream->write_queue_size == 0);
+}
+
+
+/* Implements a best effort approach to mitigating accept() EMFILE errors.
+ * We have a spare file descriptor stashed away that we close to get below
+ * the EMFILE limit. Next, we accept all pending connections and close them
+ * immediately to signal the clients that we're overloaded - and we are, but
+ * we still keep on trucking.
+ *
+ * There is one caveat: it's not reliable in a multi-threaded environment.
+ * The file descriptor limit is per process. Our party trick fails if another
+ * thread opens a file or creates a socket in the time window between us
+ * calling close() and accept().
+ */
+static int uv__emfile_trick(uv_loop_t* loop, int accept_fd) {
+  int err;
+  int emfile_fd;
+
+  if (loop->emfile_fd == -1)
+    return UV_EMFILE;
+
+  uv__close(loop->emfile_fd);
+  loop->emfile_fd = -1;
+
+  do {
+    err = uv__accept(accept_fd);
+    if (err >= 0)
+      uv__close(err);
+  } while (err >= 0 || err == UV_EINTR);
+
+  emfile_fd = uv__open_cloexec("/", O_RDONLY);
+  if (emfile_fd >= 0)
+    loop->emfile_fd = emfile_fd;
+
+  return err;
+}
+
+
+#if defined(UV_HAVE_KQUEUE)
+# define UV_DEC_BACKLOG(w) w->rcount--;
+#else
+# define UV_DEC_BACKLOG(w) /* no-op */
+#endif /* defined(UV_HAVE_KQUEUE) */
+
+
+void uv__server_io(uv_loop_t* loop, uv__io_t* w, unsigned int events) {
+  uv_stream_t* stream;
+  int err;
+
+  stream = container_of(w, uv_stream_t, io_watcher);
+  assert(events & POLLIN);
+  assert(stream->accepted_fd == -1);
+  assert(!(stream->flags & UV_HANDLE_CLOSING));
+
+  uv__io_start(stream->loop, &stream->io_watcher, POLLIN);
+
+  /* connection_cb can close the server socket while we're
+   * in the loop so check it on each iteration.
+   */
+  while (uv__stream_fd(stream) != -1) {
+    assert(stream->accepted_fd == -1);
+
+#if defined(UV_HAVE_KQUEUE)
+    if (w->rcount <= 0)
+      return;
+#endif /* defined(UV_HAVE_KQUEUE) */
+
+    err = uv__accept(uv__stream_fd(stream));
+    if (err < 0) {
+      if (err == UV_EAGAIN || err == UV__ERR(EWOULDBLOCK))
+        return;  /* Not an error. */
+
+      if (err == UV_ECONNABORTED)
+        continue;  /* Ignore. Nothing we can do about that. */
+
+      if (err == UV_EMFILE || err == UV_ENFILE) {
+        err = uv__emfile_trick(loop, uv__stream_fd(stream));
+        if (err == UV_EAGAIN || err == UV__ERR(EWOULDBLOCK))
+          break;
+      }
+
+      stream->connection_cb(stream, err);
+      continue;
+    }
+
+    UV_DEC_BACKLOG(w)
+    stream->accepted_fd = err;
+    stream->connection_cb(stream, 0);
+
+    if (stream->accepted_fd != -1) {
+      /* The user hasn't yet accepted called uv_accept() */
+      uv__io_stop(loop, &stream->io_watcher, POLLIN);
+      return;
+    }
+
+    if (stream->type == UV_TCP &&
+        (stream->flags & UV_HANDLE_TCP_SINGLE_ACCEPT)) {
+      /* Give other processes a chance to accept connections. */
+      struct timespec timeout = { 0, 1 };
+      nanosleep(&timeout, NULL);
+    }
+  }
+}
+
+
+#undef UV_DEC_BACKLOG
+
+
+int uv_accept(uv_stream_t* server, uv_stream_t* client) {
+  int err;
+
+  assert(server->loop == client->loop);
+
+  if (server->accepted_fd == -1)
+    return UV_EAGAIN;
+
+  switch (client->type) {
+    case UV_NAMED_PIPE:
+    case UV_TCP:
+      err = uv__stream_open(client,
+                            server->accepted_fd,
+                            UV_HANDLE_READABLE | UV_HANDLE_WRITABLE);
+      if (err) {
+        /* TODO handle error */
+        uv__close(server->accepted_fd);
+        goto done;
+      }
+      break;
+
+    case UV_UDP:
+      err = uv_udp_open((uv_udp_t*) client, server->accepted_fd);
+      if (err) {
+        uv__close(server->accepted_fd);
+        goto done;
+      }
+      break;
+
+    default:
+      return UV_EINVAL;
+  }
+
+  client->flags |= UV_HANDLE_BOUND;
+
+done:
+  /* Process queued fds */
+  if (server->queued_fds != NULL) {
+    uv__stream_queued_fds_t* queued_fds;
+
+    queued_fds = (uv__stream_queued_fds_t*)(server->queued_fds);
+
+    /* Read first */
+    server->accepted_fd = queued_fds->fds[0];
+
+    /* All read, free */
+    assert(queued_fds->offset > 0);
+    if (--queued_fds->offset == 0) {
+      uv__free(queued_fds);
+      server->queued_fds = NULL;
+    } else {
+      /* Shift rest */
+      memmove(queued_fds->fds,
+              queued_fds->fds + 1,
+              queued_fds->offset * sizeof(*queued_fds->fds));
+    }
+  } else {
+    server->accepted_fd = -1;
+    if (err == 0)
+      uv__io_start(server->loop, &server->io_watcher, POLLIN);
+  }
+  return err;
+}
+
+
+int uv_listen(uv_stream_t* stream, int backlog, uv_connection_cb cb) {
+  int err;
+  if (uv__is_closing(stream)) {
+    return UV_EINVAL;
+  }
+  switch (stream->type) {
+  case UV_TCP:
+    err = uv__tcp_listen((uv_tcp_t*)stream, backlog, cb);
+    break;
+
+  case UV_NAMED_PIPE:
+    err = uv__pipe_listen((uv_pipe_t*)stream, backlog, cb);
+    break;
+
+  default:
+    err = UV_EINVAL;
+  }
+
+  if (err == 0)
+    uv__handle_start(stream);
+
+  return err;
+}
+
+
+static void uv__drain(uv_stream_t* stream) {
+  uv_shutdown_t* req;
+  int err;
+
+  assert(QUEUE_EMPTY(&stream->write_queue));
+  if (!(stream->flags & UV_HANDLE_CLOSING)) {
+    uv__io_stop(stream->loop, &stream->io_watcher, POLLOUT);
+    uv__stream_osx_interrupt_select(stream);
+  }
+
+  if (!(stream->flags & UV_HANDLE_SHUTTING))
+    return;
+
+  req = stream->shutdown_req;
+  assert(req);
+
+  if ((stream->flags & UV_HANDLE_CLOSING) ||
+      !(stream->flags & UV_HANDLE_SHUT)) {
+    stream->shutdown_req = NULL;
+    stream->flags &= ~UV_HANDLE_SHUTTING;
+    uv__req_unregister(stream->loop, req);
+
+    err = 0;
+    if (stream->flags & UV_HANDLE_CLOSING)
+      /* The user destroyed the stream before we got to do the shutdown. */
+      err = UV_ECANCELED;
+    else if (shutdown(uv__stream_fd(stream), SHUT_WR))
+      err = UV__ERR(errno);
+    else /* Success. */
+      stream->flags |= UV_HANDLE_SHUT;
+
+    if (req->cb != NULL)
+      req->cb(req, err);
+  }
+}
+
+
+static ssize_t uv__writev(int fd, struct iovec* vec, size_t n) {
+  if (n == 1)
+    return write(fd, vec->iov_base, vec->iov_len);
+  else
+    return writev(fd, vec, n);
+}
+
+
+static size_t uv__write_req_size(uv_write_t* req) {
+  size_t size;
+
+  assert(req->bufs != NULL);
+  size = uv__count_bufs(req->bufs + req->write_index,
+                        req->nbufs - req->write_index);
+  assert(req->handle->write_queue_size >= size);
+
+  return size;
+}
+
+
+/* Returns 1 if all write request data has been written, or 0 if there is still
+ * more data to write.
+ *
+ * Note: the return value only says something about the *current* request.
+ * There may still be other write requests sitting in the queue.
+ */
+static int uv__write_req_update(uv_stream_t* stream,
+                                uv_write_t* req,
+                                size_t n) {
+  uv_buf_t* buf;
+  size_t len;
+
+  assert(n <= stream->write_queue_size);
+  stream->write_queue_size -= n;
+
+  buf = req->bufs + req->write_index;
+
+  do {
+    len = n < buf->len ? n : buf->len;
+    buf->base += len;
+    buf->len -= len;
+    buf += (buf->len == 0);  /* Advance to next buffer if this one is empty. */
+    n -= len;
+  } while (n > 0);
+
+  req->write_index = buf - req->bufs;
+
+  return req->write_index == req->nbufs;
+}
+
+
+static void uv__write_req_finish(uv_write_t* req) {
+  uv_stream_t* stream = req->handle;
+
+  /* Pop the req off tcp->write_queue. */
+  QUEUE_REMOVE(&req->queue);
+
+  /* Only free when there was no error. On error, we touch up write_queue_size
+   * right before making the callback. The reason we don't do that right away
+   * is that a write_queue_size > 0 is our only way to signal to the user that
+   * they should stop writing - which they should if we got an error. Something
+   * to revisit in future revisions of the libuv API.
+   */
+  if (req->error == 0) {
+    if (req->bufs != req->bufsml)
+      uv__free(req->bufs);
+    req->bufs = NULL;
+  }
+
+  /* Add it to the write_completed_queue where it will have its
+   * callback called in the near future.
+   */
+  QUEUE_INSERT_TAIL(&stream->write_completed_queue, &req->queue);
+  uv__io_feed(stream->loop, &stream->io_watcher);
+}
+
+
+static int uv__handle_fd(uv_handle_t* handle) {
+  switch (handle->type) {
+    case UV_NAMED_PIPE:
+    case UV_TCP:
+      return ((uv_stream_t*) handle)->io_watcher.fd;
+
+    case UV_UDP:
+      return ((uv_udp_t*) handle)->io_watcher.fd;
+
+    default:
+      return -1;
+  }
+}
+
+static int uv__try_write(uv_stream_t* stream,
+                         const uv_buf_t bufs[],
+                         unsigned int nbufs,
+                         uv_stream_t* send_handle) {
+  struct iovec* iov;
+  int iovmax;
+  int iovcnt;
+  ssize_t n;
+
+  /*
+   * Cast to iovec. We had to have our own uv_buf_t instead of iovec
+   * because Windows's WSABUF is not an iovec.
+   */
+  iov = (struct iovec*) bufs;
+  iovcnt = nbufs;
+
+  iovmax = uv__getiovmax();
+
+  /* Limit iov count to avoid EINVALs from writev() */
+  if (iovcnt > iovmax)
+    iovcnt = iovmax;
+
+  /*
+   * Now do the actual writev. Note that we've been updating the pointers
+   * inside the iov each time we write. So there is no need to offset it.
+   */
+  if (send_handle != NULL) {
+    int fd_to_send;
+    struct msghdr msg;
+    struct cmsghdr *cmsg;
+    union {
+      char data[64];
+      struct cmsghdr alias;
+    } scratch;
+
+    if (uv__is_closing(send_handle))
+      return UV_EBADF;
+
+    fd_to_send = uv__handle_fd((uv_handle_t*) send_handle);
+
+    memset(&scratch, 0, sizeof(scratch));
+
+    assert(fd_to_send >= 0);
+
+    msg.msg_name = NULL;
+    msg.msg_namelen = 0;
+    msg.msg_iov = iov;
+    msg.msg_iovlen = iovcnt;
+    msg.msg_flags = 0;
+
+    msg.msg_control = &scratch.alias;
+    msg.msg_controllen = CMSG_SPACE(sizeof(fd_to_send));
+
+    cmsg = CMSG_FIRSTHDR(&msg);
+    cmsg->cmsg_level = SOL_SOCKET;
+    cmsg->cmsg_type = SCM_RIGHTS;
+    cmsg->cmsg_len = CMSG_LEN(sizeof(fd_to_send));
+
+    /* silence aliasing warning */
+    {
+      void* pv = CMSG_DATA(cmsg);
+      int* pi = (int*)pv;
+      *pi = fd_to_send;
+    }
+
+    do
+      n = sendmsg(uv__stream_fd(stream), &msg, 0);
+    while (n == -1 && errno == EINTR);
+  } else {
+    do
+      n = uv__writev(uv__stream_fd(stream), iov, iovcnt);
+    while (n == -1 && errno == EINTR);
+  }
+
+  if (n >= 0)
+    return n;
+
+  if (errno == EAGAIN || errno == EWOULDBLOCK || errno == ENOBUFS)
+    return UV_EAGAIN;
+
+#ifdef __APPLE__
+  /* macOS versions 10.10 and 10.15 - and presumbaly 10.11 to 10.14, too -
+   * have a bug where a race condition causes the kernel to return EPROTOTYPE
+   * because the socket isn't fully constructed. It's probably the result of
+   * the peer closing the connection and that is why libuv translates it to
+   * ECONNRESET. Previously, libuv retried until the EPROTOTYPE error went
+   * away but some VPN software causes the same behavior except the error is
+   * permanent, not transient, turning the retry mechanism into an infinite
+   * loop. See https://github.com/libuv/libuv/pull/482.
+   */
+  if (errno == EPROTOTYPE)
+    return UV_ECONNRESET;
+#endif  /* __APPLE__ */
+
+  return UV__ERR(errno);
+}
+
+static void uv__write(uv_stream_t* stream) {
+  QUEUE* q;
+  uv_write_t* req;
+  ssize_t n;
+
+  assert(uv__stream_fd(stream) >= 0);
+
+  for (;;) {
+    if (QUEUE_EMPTY(&stream->write_queue))
+      return;
+
+    q = QUEUE_HEAD(&stream->write_queue);
+    req = QUEUE_DATA(q, uv_write_t, queue);
+    assert(req->handle == stream);
+
+    n = uv__try_write(stream,
+                      &(req->bufs[req->write_index]),
+                      req->nbufs - req->write_index,
+                      req->send_handle);
+
+    /* Ensure the handle isn't sent again in case this is a partial write. */
+    if (n >= 0) {
+      req->send_handle = NULL;
+      if (uv__write_req_update(stream, req, n)) {
+        uv__write_req_finish(req);
+        return;  /* TODO(bnoordhuis) Start trying to write the next request. */
+      }
+    } else if (n != UV_EAGAIN)
+      break;
+
+    /* If this is a blocking stream, try again. */
+    if (stream->flags & UV_HANDLE_BLOCKING_WRITES)
+      continue;
+
+    /* We're not done. */
+    uv__io_start(stream->loop, &stream->io_watcher, POLLOUT);
+
+    /* Notify select() thread about state change */
+    uv__stream_osx_interrupt_select(stream);
+
+    return;
+  }
+
+  req->error = n;
+  uv__write_req_finish(req);
+  uv__io_stop(stream->loop, &stream->io_watcher, POLLOUT);
+  uv__stream_osx_interrupt_select(stream);
+}
+
+
+static void uv__write_callbacks(uv_stream_t* stream) {
+  uv_write_t* req;
+  QUEUE* q;
+  QUEUE pq;
+
+  if (QUEUE_EMPTY(&stream->write_completed_queue))
+    return;
+
+// FIXME: GCC 12.1 gives a possibly real warning, but we don't know how to fix
+// it
+#if __GNUC__ >= 12
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wdangling-pointer="
+#endif  // __GNUC__ >= 12
+  QUEUE_MOVE(&stream->write_completed_queue, &pq);
+#if __GNUC__ >= 12
+#pragma GCC diagnostic pop
+#endif  // __GNUC__ >= 12
+
+  while (!QUEUE_EMPTY(&pq)) {
+    /* Pop a req off write_completed_queue. */
+    q = QUEUE_HEAD(&pq);
+    req = QUEUE_DATA(q, uv_write_t, queue);
+    QUEUE_REMOVE(q);
+    uv__req_unregister(stream->loop, req);
+
+    if (req->bufs != NULL) {
+      stream->write_queue_size -= uv__write_req_size(req);
+      if (req->bufs != req->bufsml)
+        uv__free(req->bufs);
+      req->bufs = NULL;
+    }
+
+    /* NOTE: call callback AFTER freeing the request data. */
+    if (req->cb)
+      req->cb(req, req->error);
+  }
+}
+
+
+static void uv__stream_eof(uv_stream_t* stream, const uv_buf_t* buf) {
+  stream->flags |= UV_HANDLE_READ_EOF;
+  stream->flags &= ~UV_HANDLE_READING;
+  uv__io_stop(stream->loop, &stream->io_watcher, POLLIN);
+  uv__handle_stop(stream);
+  uv__stream_osx_interrupt_select(stream);
+  stream->read_cb(stream, UV_EOF, buf);
+}
+
+
+static int uv__stream_queue_fd(uv_stream_t* stream, int fd) {
+  uv__stream_queued_fds_t* queued_fds;
+  unsigned int queue_size;
+
+  queued_fds = (uv__stream_queued_fds_t*)stream->queued_fds;
+  if (queued_fds == NULL) {
+    queue_size = 8;
+    queued_fds = (uv__stream_queued_fds_t*)
+        uv__malloc((queue_size - 1) * sizeof(*queued_fds->fds) +
+                   sizeof(*queued_fds));
+    if (queued_fds == NULL)
+      return UV_ENOMEM;
+    queued_fds->size = queue_size;
+    queued_fds->offset = 0;
+    stream->queued_fds = queued_fds;
+
+    /* Grow */
+  } else if (queued_fds->size == queued_fds->offset) {
+    queue_size = queued_fds->size + 8;
+    queued_fds = (uv__stream_queued_fds_t*)
+        uv__realloc(queued_fds, (queue_size - 1) * sizeof(*queued_fds->fds) +
+                    sizeof(*queued_fds));
+
+    /*
+     * Allocation failure, report back.
+     * NOTE: if it is fatal - sockets will be closed in uv__stream_close
+     */
+    if (queued_fds == NULL)
+      return UV_ENOMEM;
+    queued_fds->size = queue_size;
+    stream->queued_fds = queued_fds;
+  }
+
+  /* Put fd in a queue */
+  queued_fds->fds[queued_fds->offset++] = fd;
+
+  return 0;
+}
+
+
+#if defined(__PASE__)
+/* on IBMi PASE the control message length can not exceed 256. */
+# define UV__CMSG_FD_COUNT 60
+#else
+# define UV__CMSG_FD_COUNT 64
+#endif
+#define UV__CMSG_FD_SIZE (UV__CMSG_FD_COUNT * sizeof(int))
+
+
+static int uv__stream_recv_cmsg(uv_stream_t* stream, struct msghdr* msg) {
+  struct cmsghdr* cmsg;
+
+  for (cmsg = CMSG_FIRSTHDR(msg); cmsg != NULL; cmsg = CMSG_NXTHDR(msg, cmsg)) {
+    char* start;
+    char* end;
+    int err;
+    void* pv;
+    int* pi;
+    unsigned int i;
+    unsigned int count;
+
+    if (cmsg->cmsg_type != SCM_RIGHTS) {
+      fprintf(stderr, "ignoring non-SCM_RIGHTS ancillary data: %d\n",
+          cmsg->cmsg_type);
+      continue;
+    }
+
+    /* silence aliasing warning */
+    pv = CMSG_DATA(cmsg);
+    pi = (int*)pv;
+
+    /* Count available fds */
+    start = (char*) cmsg;
+    end = (char*) cmsg + cmsg->cmsg_len;
+    count = 0;
+    while (start + CMSG_LEN(count * sizeof(*pi)) < end)
+      count++;
+    assert(start + CMSG_LEN(count * sizeof(*pi)) == end);
+
+    for (i = 0; i < count; i++) {
+      /* Already has accepted fd, queue now */
+      if (stream->accepted_fd != -1) {
+        err = uv__stream_queue_fd(stream, pi[i]);
+        if (err != 0) {
+          /* Close rest */
+          for (; i < count; i++)
+            uv__close(pi[i]);
+          return err;
+        }
+      } else {
+        stream->accepted_fd = pi[i];
+      }
+    }
+  }
+
+  return 0;
+}
+
+
+#ifdef __clang__
+# pragma clang diagnostic push
+# pragma clang diagnostic ignored "-Wgnu-folding-constant"
+# pragma clang diagnostic ignored "-Wvla-extension"
+#endif
+
+static void uv__read(uv_stream_t* stream) {
+  uv_buf_t buf;
+  ssize_t nread;
+  struct msghdr msg;
+  char cmsg_space[CMSG_SPACE(UV__CMSG_FD_SIZE)];
+  int count;
+  int err;
+  int is_ipc;
+
+  stream->flags &= ~UV_HANDLE_READ_PARTIAL;
+
+  /* Prevent loop starvation when the data comes in as fast as (or faster than)
+   * we can read it. XXX Need to rearm fd if we switch to edge-triggered I/O.
+   */
+  count = 32;
+
+  is_ipc = stream->type == UV_NAMED_PIPE && ((uv_pipe_t*) stream)->ipc;
+
+  /* XXX: Maybe instead of having UV_HANDLE_READING we just test if
+   * tcp->read_cb is NULL or not?
+   */
+  while (stream->read_cb
+      && (stream->flags & UV_HANDLE_READING)
+      && (count-- > 0)) {
+    assert(stream->alloc_cb != NULL);
+
+    buf = uv_buf_init(NULL, 0);
+    stream->alloc_cb((uv_handle_t*)stream, 64 * 1024, &buf);
+    if (buf.base == NULL || buf.len == 0) {
+      /* User indicates it can't or won't handle the read. */
+      stream->read_cb(stream, UV_ENOBUFS, &buf);
+      return;
+    }
+
+    assert(buf.base != NULL);
+    assert(uv__stream_fd(stream) >= 0);
+
+    if (!is_ipc) {
+      do {
+        nread = read(uv__stream_fd(stream), buf.base, buf.len);
+      }
+      while (nread < 0 && errno == EINTR);
+    } else {
+      /* ipc uses recvmsg */
+      msg.msg_flags = 0;
+      msg.msg_iov = (struct iovec*) &buf;
+      msg.msg_iovlen = 1;
+      msg.msg_name = NULL;
+      msg.msg_namelen = 0;
+      /* Set up to receive a descriptor even if one isn't in the message */
+      msg.msg_controllen = sizeof(cmsg_space);
+      msg.msg_control = cmsg_space;
+
+      do {
+        nread = uv__recvmsg(uv__stream_fd(stream), &msg, 0);
+      }
+      while (nread < 0 && errno == EINTR);
+    }
+
+    if (nread < 0) {
+      /* Error */
+      if (errno == EAGAIN || errno == EWOULDBLOCK) {
+        /* Wait for the next one. */
+        if (stream->flags & UV_HANDLE_READING) {
+          uv__io_start(stream->loop, &stream->io_watcher, POLLIN);
+          uv__stream_osx_interrupt_select(stream);
+        }
+        stream->read_cb(stream, 0, &buf);
+#if defined(__CYGWIN__) || defined(__MSYS__)
+      } else if (errno == ECONNRESET && stream->type == UV_NAMED_PIPE) {
+        uv__stream_eof(stream, &buf);
+        return;
+#endif
+      } else {
+        /* Error. User should call uv_close(). */
+        stream->flags &= ~(UV_HANDLE_READABLE | UV_HANDLE_WRITABLE);
+        stream->read_cb(stream, UV__ERR(errno), &buf);
+        if (stream->flags & UV_HANDLE_READING) {
+          stream->flags &= ~UV_HANDLE_READING;
+          uv__io_stop(stream->loop, &stream->io_watcher, POLLIN);
+          uv__handle_stop(stream);
+          uv__stream_osx_interrupt_select(stream);
+        }
+      }
+      return;
+    } else if (nread == 0) {
+      uv__stream_eof(stream, &buf);
+      return;
+    } else {
+      /* Successful read */
+      ssize_t buflen = buf.len;
+
+      if (is_ipc) {
+        err = uv__stream_recv_cmsg(stream, &msg);
+        if (err != 0) {
+          stream->read_cb(stream, err, &buf);
+          return;
+        }
+      }
+
+#if defined(__MVS__)
+      if (is_ipc && msg.msg_controllen > 0) {
+        uv_buf_t blankbuf;
+        int nread;
+        struct iovec *old;
+
+        blankbuf.base = 0;
+        blankbuf.len = 0;
+        old = msg.msg_iov;
+        msg.msg_iov = (struct iovec*) &blankbuf;
+        nread = 0;
+        do {
+          nread = uv__recvmsg(uv__stream_fd(stream), &msg, 0);
+          err = uv__stream_recv_cmsg(stream, &msg);
+          if (err != 0) {
+            stream->read_cb(stream, err, &buf);
+            msg.msg_iov = old;
+            return;
+          }
+        } while (nread == 0 && msg.msg_controllen > 0);
+        msg.msg_iov = old;
+      }
+#endif
+      stream->read_cb(stream, nread, &buf);
+
+      /* Return if we didn't fill the buffer, there is no more data to read. */
+      if (nread < buflen) {
+        stream->flags |= UV_HANDLE_READ_PARTIAL;
+        return;
+      }
+    }
+  }
+}
+
+
+#ifdef __clang__
+# pragma clang diagnostic pop
+#endif
+
+#undef UV__CMSG_FD_COUNT
+#undef UV__CMSG_FD_SIZE
+
+
+int uv_shutdown(uv_shutdown_t* req, uv_stream_t* stream, uv_shutdown_cb cb) {
+  assert(stream->type == UV_TCP ||
+         stream->type == UV_TTY ||
+         stream->type == UV_NAMED_PIPE);
+
+  if (!(stream->flags & UV_HANDLE_WRITABLE) ||
+      stream->flags & UV_HANDLE_SHUT ||
+      stream->flags & UV_HANDLE_SHUTTING ||
+      uv__is_closing(stream)) {
+    return UV_ENOTCONN;
+  }
+
+  assert(uv__stream_fd(stream) >= 0);
+
+  /* Initialize request. The `shutdown(2)` call will always be deferred until
+   * `uv__drain`, just before the callback is run. */
+  uv__req_init(stream->loop, req, UV_SHUTDOWN);
+  req->handle = stream;
+  req->cb = cb;
+  stream->shutdown_req = req;
+  stream->flags |= UV_HANDLE_SHUTTING;
+  stream->flags &= ~UV_HANDLE_WRITABLE;
+
+  if (QUEUE_EMPTY(&stream->write_queue))
+    uv__io_feed(stream->loop, &stream->io_watcher);
+
+  return 0;
+}
+
+
+static void uv__stream_io(uv_loop_t* loop, uv__io_t* w, unsigned int events) {
+  uv_stream_t* stream;
+
+  stream = container_of(w, uv_stream_t, io_watcher);
+
+  assert(stream->type == UV_TCP ||
+         stream->type == UV_NAMED_PIPE ||
+         stream->type == UV_TTY);
+  assert(!(stream->flags & UV_HANDLE_CLOSING));
+
+  if (stream->connect_req) {
+    uv__stream_connect(stream);
+    return;
+  }
+
+  assert(uv__stream_fd(stream) >= 0);
+
+  /* Ignore POLLHUP here. Even if it's set, there may still be data to read. */
+  if (events & (POLLIN | POLLERR | POLLHUP))
+    uv__read(stream);
+
+  if (uv__stream_fd(stream) == -1)
+    return;  /* read_cb closed stream. */
+
+  /* Short-circuit iff POLLHUP is set, the user is still interested in read
+   * events and uv__read() reported a partial read but not EOF. If the EOF
+   * flag is set, uv__read() called read_cb with err=UV_EOF and we don't
+   * have to do anything. If the partial read flag is not set, we can't
+   * report the EOF yet because there is still data to read.
+   */
+  if ((events & POLLHUP) &&
+      (stream->flags & UV_HANDLE_READING) &&
+      (stream->flags & UV_HANDLE_READ_PARTIAL) &&
+      !(stream->flags & UV_HANDLE_READ_EOF)) {
+    uv_buf_t buf = { NULL, 0 };
+    uv__stream_eof(stream, &buf);
+  }
+
+  if (uv__stream_fd(stream) == -1)
+    return;  /* read_cb closed stream. */
+
+  if (events & (POLLOUT | POLLERR | POLLHUP)) {
+    uv__write(stream);
+    uv__write_callbacks(stream);
+
+    /* Write queue drained. */
+    if (QUEUE_EMPTY(&stream->write_queue))
+      uv__drain(stream);
+  }
+}
+
+
+/**
+ * We get called here from directly following a call to connect(2).
+ * In order to determine if we've errored out or succeeded must call
+ * getsockopt.
+ */
+static void uv__stream_connect(uv_stream_t* stream) {
+  int error;
+  uv_connect_t* req = stream->connect_req;
+  socklen_t errorsize = sizeof(int);
+
+  assert(stream->type == UV_TCP || stream->type == UV_NAMED_PIPE);
+  assert(req);
+
+  if (stream->delayed_error) {
+    /* To smooth over the differences between unixes errors that
+     * were reported synchronously on the first connect can be delayed
+     * until the next tick--which is now.
+     */
+    error = stream->delayed_error;
+    stream->delayed_error = 0;
+  } else {
+    /* Normal situation: we need to get the socket error from the kernel. */
+    assert(uv__stream_fd(stream) >= 0);
+    getsockopt(uv__stream_fd(stream),
+               SOL_SOCKET,
+               SO_ERROR,
+               &error,
+               &errorsize);
+    error = UV__ERR(error);
+  }
+
+  if (error == UV__ERR(EINPROGRESS))
+    return;
+
+  stream->connect_req = NULL;
+  uv__req_unregister(stream->loop, req);
+
+  if (error < 0 || QUEUE_EMPTY(&stream->write_queue)) {
+    uv__io_stop(stream->loop, &stream->io_watcher, POLLOUT);
+  }
+
+  if (req->cb)
+    req->cb(req, error);
+
+  if (uv__stream_fd(stream) == -1)
+    return;
+
+  if (error < 0) {
+    uv__stream_flush_write_queue(stream, UV_ECANCELED);
+    uv__write_callbacks(stream);
+  }
+}
+
+
+static int uv__check_before_write(uv_stream_t* stream,
+                                  unsigned int nbufs,
+                                  uv_stream_t* send_handle) {
+  assert(nbufs > 0);
+  assert((stream->type == UV_TCP ||
+          stream->type == UV_NAMED_PIPE ||
+          stream->type == UV_TTY) &&
+         "uv_write (unix) does not yet support other types of streams");
+
+  if (uv__stream_fd(stream) < 0)
+    return UV_EBADF;
+
+  if (!(stream->flags & UV_HANDLE_WRITABLE))
+    return UV_EPIPE;
+
+  if (send_handle != NULL) {
+    if (stream->type != UV_NAMED_PIPE || !((uv_pipe_t*)stream)->ipc)
+      return UV_EINVAL;
+
+    /* XXX We abuse uv_write2() to send over UDP handles to child processes.
+     * Don't call uv__stream_fd() on those handles, it's a macro that on OS X
+     * evaluates to a function that operates on a uv_stream_t with a couple of
+     * OS X specific fields. On other Unices it does (handle)->io_watcher.fd,
+     * which works but only by accident.
+     */
+    if (uv__handle_fd((uv_handle_t*) send_handle) < 0)
+      return UV_EBADF;
+
+#if defined(__CYGWIN__) || defined(__MSYS__)
+    /* Cygwin recvmsg always sets msg_controllen to zero, so we cannot send it.
+       See https://github.com/mirror/newlib-cygwin/blob/86fc4bf0/winsup/cygwin/fhandler_socket.cc#L1736-L1743 */
+    return UV_ENOSYS;
+#endif
+  }
+
+  return 0;
+}
+
+int uv_write2(uv_write_t* req,
+              uv_stream_t* stream,
+              const uv_buf_t bufs[],
+              unsigned int nbufs,
+              uv_stream_t* send_handle,
+              uv_write_cb cb) {
+  int empty_queue;
+  int err;
+
+  err = uv__check_before_write(stream, nbufs, send_handle);
+  if (err < 0)
+    return err;
+
+  /* It's legal for write_queue_size > 0 even when the write_queue is empty;
+   * it means there are error-state requests in the write_completed_queue that
+   * will touch up write_queue_size later, see also uv__write_req_finish().
+   * We could check that write_queue is empty instead but that implies making
+   * a write() syscall when we know that the handle is in error mode.
+   */
+  empty_queue = (stream->write_queue_size == 0);
+
+  /* Initialize the req */
+  uv__req_init(stream->loop, req, UV_WRITE);
+  req->cb = cb;
+  req->handle = stream;
+  req->error = 0;
+  req->send_handle = send_handle;
+  QUEUE_INIT(&req->queue);
+
+  req->bufs = req->bufsml;
+  if (nbufs > ARRAY_SIZE(req->bufsml))
+    req->bufs = (uv_buf_t*)uv__malloc(nbufs * sizeof(bufs[0]));
+
+  if (req->bufs == NULL)
+    return UV_ENOMEM;
+
+  memcpy(req->bufs, bufs, nbufs * sizeof(bufs[0]));
+  req->nbufs = nbufs;
+  req->write_index = 0;
+  stream->write_queue_size += uv__count_bufs(bufs, nbufs);
+
+  /* Append the request to write_queue. */
+  QUEUE_INSERT_TAIL(&stream->write_queue, &req->queue);
+
+  /* If the queue was empty when this function began, we should attempt to
+   * do the write immediately. Otherwise start the write_watcher and wait
+   * for the fd to become writable.
+   */
+  if (stream->connect_req) {
+    /* Still connecting, do nothing. */
+  }
+  else if (empty_queue) {
+    uv__write(stream);
+  }
+  else {
+    /*
+     * blocking streams should never have anything in the queue.
+     * if this assert fires then somehow the blocking stream isn't being
+     * sufficiently flushed in uv__write.
+     */
+    assert(!(stream->flags & UV_HANDLE_BLOCKING_WRITES));
+    uv__io_start(stream->loop, &stream->io_watcher, POLLOUT);
+    uv__stream_osx_interrupt_select(stream);
+  }
+
+  return 0;
+}
+
+
+/* The buffers to be written must remain valid until the callback is called.
+ * This is not required for the uv_buf_t array.
+ */
+int uv_write(uv_write_t* req,
+             uv_stream_t* handle,
+             const uv_buf_t bufs[],
+             unsigned int nbufs,
+             uv_write_cb cb) {
+  return uv_write2(req, handle, bufs, nbufs, NULL, cb);
+}
+
+
+int uv_try_write(uv_stream_t* stream,
+                 const uv_buf_t bufs[],
+                 unsigned int nbufs) {
+  return uv_try_write2(stream, bufs, nbufs, NULL);
+}
+
+
+int uv_try_write2(uv_stream_t* stream,
+                  const uv_buf_t bufs[],
+                  unsigned int nbufs,
+                  uv_stream_t* send_handle) {
+  int err;
+
+  /* Connecting or already writing some data */
+  if (stream->connect_req != NULL || stream->write_queue_size != 0)
+    return UV_EAGAIN;
+
+  err = uv__check_before_write(stream, nbufs, NULL);
+  if (err < 0)
+    return err;
+
+  return uv__try_write(stream, bufs, nbufs, send_handle);
+}
+
+
+int uv__read_start(uv_stream_t* stream,
+                   uv_alloc_cb alloc_cb,
+                   uv_read_cb read_cb) {
+  assert(stream->type == UV_TCP || stream->type == UV_NAMED_PIPE ||
+      stream->type == UV_TTY);
+
+  /* The UV_HANDLE_READING flag is irrelevant of the state of the stream - it
+   * just expresses the desired state of the user. */
+  stream->flags |= UV_HANDLE_READING;
+  stream->flags &= ~UV_HANDLE_READ_EOF;
+
+  /* TODO: try to do the read inline? */
+  assert(uv__stream_fd(stream) >= 0);
+  assert(alloc_cb);
+
+  stream->read_cb = read_cb;
+  stream->alloc_cb = alloc_cb;
+
+  uv__io_start(stream->loop, &stream->io_watcher, POLLIN);
+  uv__handle_start(stream);
+  uv__stream_osx_interrupt_select(stream);
+
+  return 0;
+}
+
+
+int uv_read_stop(uv_stream_t* stream) {
+  if (!(stream->flags & UV_HANDLE_READING))
+    return 0;
+
+  stream->flags &= ~UV_HANDLE_READING;
+  uv__io_stop(stream->loop, &stream->io_watcher, POLLIN);
+  uv__handle_stop(stream);
+  uv__stream_osx_interrupt_select(stream);
+
+  stream->read_cb = NULL;
+  stream->alloc_cb = NULL;
+  return 0;
+}
+
+
+int uv_is_readable(const uv_stream_t* stream) {
+  return !!(stream->flags & UV_HANDLE_READABLE);
+}
+
+
+int uv_is_writable(const uv_stream_t* stream) {
+  return !!(stream->flags & UV_HANDLE_WRITABLE);
+}
+
+
+#if defined(__APPLE__)
+int uv___stream_fd(const uv_stream_t* handle) {
+  const uv__stream_select_t* s;
+
+  assert(handle->type == UV_TCP ||
+         handle->type == UV_TTY ||
+         handle->type == UV_NAMED_PIPE);
+
+  s = (const uv__stream_select_t*)handle->select;
+  if (s != NULL)
+    return s->fd;
+
+  return handle->io_watcher.fd;
+}
+#endif /* defined(__APPLE__) */
+
+
+void uv__stream_close(uv_stream_t* handle) {
+  unsigned int i;
+  uv__stream_queued_fds_t* queued_fds;
+
+#if defined(__APPLE__)
+  /* Terminate select loop first */
+  if (handle->select != NULL) {
+    uv__stream_select_t* s;
+
+    s = (uv__stream_select_t*)handle->select;
+
+    uv_sem_post(&s->close_sem);
+    uv_sem_post(&s->async_sem);
+    uv__stream_osx_interrupt_select(handle);
+    uv_thread_join(&s->thread);
+    uv_sem_destroy(&s->close_sem);
+    uv_sem_destroy(&s->async_sem);
+    uv__close(s->fake_fd);
+    uv__close(s->int_fd);
+    uv_close((uv_handle_t*) &s->async, uv__stream_osx_cb_close);
+
+    handle->select = NULL;
+  }
+#endif /* defined(__APPLE__) */
+
+  uv__io_close(handle->loop, &handle->io_watcher);
+  uv_read_stop(handle);
+  uv__handle_stop(handle);
+  handle->flags &= ~(UV_HANDLE_READABLE | UV_HANDLE_WRITABLE);
+
+  if (handle->io_watcher.fd != -1) {
+    /* Don't close stdio file descriptors.  Nothing good comes from it. */
+    if (handle->io_watcher.fd > STDERR_FILENO)
+      uv__close(handle->io_watcher.fd);
+    handle->io_watcher.fd = -1;
+  }
+
+  if (handle->accepted_fd != -1) {
+    uv__close(handle->accepted_fd);
+    handle->accepted_fd = -1;
+  }
+
+  /* Close all queued fds */
+  if (handle->queued_fds != NULL) {
+    queued_fds = (uv__stream_queued_fds_t*)(handle->queued_fds);
+    for (i = 0; i < queued_fds->offset; i++)
+      uv__close(queued_fds->fds[i]);
+    uv__free(handle->queued_fds);
+    handle->queued_fds = NULL;
+  }
+
+  assert(!uv__io_active(&handle->io_watcher, POLLIN | POLLOUT));
+}
+
+
+int uv_stream_set_blocking(uv_stream_t* handle, int blocking) {
+  /* Don't need to check the file descriptor, uv__nonblock()
+   * will fail with EBADF if it's not valid.
+   */
+  return uv__nonblock(uv__stream_fd(handle), !blocking);
+}
diff --git a/wpinet/src/main/native/thirdparty/libuv/src/unix/tcp.cpp b/wpinet/src/main/native/thirdparty/libuv/src/unix/tcp.cpp
new file mode 100644
index 0000000..73fc657
--- /dev/null
+++ b/wpinet/src/main/native/thirdparty/libuv/src/unix/tcp.cpp
@@ -0,0 +1,519 @@
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include "uv.h"
+#include "internal.h"
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <assert.h>
+#include <errno.h>
+
+
+static int new_socket(uv_tcp_t* handle, int domain, unsigned long flags) {
+  struct sockaddr_storage saddr;
+  socklen_t slen;
+  int sockfd;
+  int err;
+
+  err = uv__socket(domain, SOCK_STREAM, 0);
+  if (err < 0)
+    return err;
+  sockfd = err;
+
+  err = uv__stream_open((uv_stream_t*) handle, sockfd, flags);
+  if (err) {
+    uv__close(sockfd);
+    return err;
+  }
+
+  if (flags & UV_HANDLE_BOUND) {
+    /* Bind this new socket to an arbitrary port */
+    slen = sizeof(saddr);
+    memset(&saddr, 0, sizeof(saddr));
+    if (getsockname(uv__stream_fd(handle), (struct sockaddr*) &saddr, &slen)) {
+      uv__close(sockfd);
+      return UV__ERR(errno);
+    }
+
+    if (bind(uv__stream_fd(handle), (struct sockaddr*) &saddr, slen)) {
+      uv__close(sockfd);
+      return UV__ERR(errno);
+    }
+  }
+
+  return 0;
+}
+
+
+static int maybe_new_socket(uv_tcp_t* handle, int domain, unsigned long flags) {
+  struct sockaddr_storage saddr;
+  socklen_t slen;
+
+  if (domain == AF_UNSPEC) {
+    handle->flags |= flags;
+    return 0;
+  }
+
+  if (uv__stream_fd(handle) != -1) {
+
+    if (flags & UV_HANDLE_BOUND) {
+
+      if (handle->flags & UV_HANDLE_BOUND) {
+        /* It is already bound to a port. */
+        handle->flags |= flags;
+        return 0;
+      }
+
+      /* Query to see if tcp socket is bound. */
+      slen = sizeof(saddr);
+      memset(&saddr, 0, sizeof(saddr));
+      if (getsockname(uv__stream_fd(handle), (struct sockaddr*) &saddr, &slen))
+        return UV__ERR(errno);
+
+      if ((saddr.ss_family == AF_INET6 &&
+          ((struct sockaddr_in6*) &saddr)->sin6_port != 0) ||
+          (saddr.ss_family == AF_INET &&
+          ((struct sockaddr_in*) &saddr)->sin_port != 0)) {
+        /* Handle is already bound to a port. */
+        handle->flags |= flags;
+        return 0;
+      }
+
+      /* Bind to arbitrary port */
+      if (bind(uv__stream_fd(handle), (struct sockaddr*) &saddr, slen))
+        return UV__ERR(errno);
+    }
+
+    handle->flags |= flags;
+    return 0;
+  }
+
+  return new_socket(handle, domain, flags);
+}
+
+
+int uv_tcp_init_ex(uv_loop_t* loop, uv_tcp_t* tcp, unsigned int flags) {
+  int domain;
+
+  /* Use the lower 8 bits for the domain */
+  domain = flags & 0xFF;
+  if (domain != AF_INET && domain != AF_INET6 && domain != AF_UNSPEC)
+    return UV_EINVAL;
+
+  if (flags & ~0xFF)
+    return UV_EINVAL;
+
+  uv__stream_init(loop, (uv_stream_t*)tcp, UV_TCP);
+
+  /* If anything fails beyond this point we need to remove the handle from
+   * the handle queue, since it was added by uv__handle_init in uv_stream_init.
+   */
+
+  if (domain != AF_UNSPEC) {
+    int err = maybe_new_socket(tcp, domain, 0);
+    if (err) {
+      QUEUE_REMOVE(&tcp->handle_queue);
+      return err;
+    }
+  }
+
+  return 0;
+}
+
+
+int uv_tcp_init(uv_loop_t* loop, uv_tcp_t* tcp) {
+  return uv_tcp_init_ex(loop, tcp, AF_UNSPEC);
+}
+
+
+int uv__tcp_bind(uv_tcp_t* tcp,
+                 const struct sockaddr* addr,
+                 unsigned int addrlen,
+                 unsigned int flags) {
+  int err;
+  int on;
+
+  /* Cannot set IPv6-only mode on non-IPv6 socket. */
+  if ((flags & UV_TCP_IPV6ONLY) && addr->sa_family != AF_INET6)
+    return UV_EINVAL;
+
+  err = maybe_new_socket(tcp, addr->sa_family, 0);
+  if (err)
+    return err;
+
+  on = 1;
+  if (setsockopt(tcp->io_watcher.fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)))
+    return UV__ERR(errno);
+
+#ifndef __OpenBSD__
+#ifdef IPV6_V6ONLY
+  if (addr->sa_family == AF_INET6) {
+    on = (flags & UV_TCP_IPV6ONLY) != 0;
+    if (setsockopt(tcp->io_watcher.fd,
+                   IPPROTO_IPV6,
+                   IPV6_V6ONLY,
+                   &on,
+                   sizeof on) == -1) {
+#if defined(__MVS__)
+      if (errno == EOPNOTSUPP)
+        return UV_EINVAL;
+#endif
+      return UV__ERR(errno);
+    }
+  }
+#endif
+#endif
+
+  errno = 0;
+  err = bind(tcp->io_watcher.fd, addr, addrlen);
+  if (err == -1 && errno != EADDRINUSE) {
+    if (errno == EAFNOSUPPORT)
+      /* OSX, other BSDs and SunoS fail with EAFNOSUPPORT when binding a
+       * socket created with AF_INET to an AF_INET6 address or vice versa. */
+      return UV_EINVAL;
+    return UV__ERR(errno);
+  }
+  tcp->delayed_error = (err == -1) ? UV__ERR(errno) : 0;
+
+  tcp->flags |= UV_HANDLE_BOUND;
+  if (addr->sa_family == AF_INET6)
+    tcp->flags |= UV_HANDLE_IPV6;
+
+  return 0;
+}
+
+
+int uv__tcp_connect(uv_connect_t* req,
+                    uv_tcp_t* handle,
+                    const struct sockaddr* addr,
+                    unsigned int addrlen,
+                    uv_connect_cb cb) {
+  int err;
+  int r;
+
+  assert(handle->type == UV_TCP);
+
+  if (handle->connect_req != NULL)
+    return UV_EALREADY;  /* FIXME(bnoordhuis) UV_EINVAL or maybe UV_EBUSY. */
+
+  if (handle->delayed_error != 0)
+    goto out;
+
+  err = maybe_new_socket(handle,
+                         addr->sa_family,
+                         UV_HANDLE_READABLE | UV_HANDLE_WRITABLE);
+  if (err)
+    return err;
+
+  do {
+    errno = 0;
+    r = connect(uv__stream_fd(handle), addr, addrlen);
+  } while (r == -1 && errno == EINTR);
+
+  /* We not only check the return value, but also check the errno != 0.
+   * Because in rare cases connect() will return -1 but the errno
+   * is 0 (for example, on Android 4.3, OnePlus phone A0001_12_150227)
+   * and actually the tcp three-way handshake is completed.
+   */
+  if (r == -1 && errno != 0) {
+    if (errno == EINPROGRESS)
+      ; /* not an error */
+    else if (errno == ECONNREFUSED
+#if defined(__OpenBSD__)
+      || errno == EINVAL
+#endif
+      )
+    /* If we get ECONNREFUSED (Solaris) or EINVAL (OpenBSD) wait until the
+     * next tick to report the error. Solaris and OpenBSD wants to report
+     * immediately -- other unixes want to wait.
+     */
+      handle->delayed_error = UV__ERR(ECONNREFUSED);
+    else
+      return UV__ERR(errno);
+  }
+
+out:
+
+  uv__req_init(handle->loop, req, UV_CONNECT);
+  req->cb = cb;
+  req->handle = (uv_stream_t*) handle;
+  QUEUE_INIT(&req->queue);
+  handle->connect_req = req;
+
+  uv__io_start(handle->loop, &handle->io_watcher, POLLOUT);
+
+  if (handle->delayed_error)
+    uv__io_feed(handle->loop, &handle->io_watcher);
+
+  return 0;
+}
+
+
+int uv_tcp_open(uv_tcp_t* handle, uv_os_sock_t sock) {
+  int err;
+
+  if (uv__fd_exists(handle->loop, sock))
+    return UV_EEXIST;
+
+  err = uv__nonblock(sock, 1);
+  if (err)
+    return err;
+
+  return uv__stream_open((uv_stream_t*)handle,
+                         sock,
+                         UV_HANDLE_READABLE | UV_HANDLE_WRITABLE);
+}
+
+
+int uv_tcp_getsockname(const uv_tcp_t* handle,
+                       struct sockaddr* name,
+                       int* namelen) {
+
+  if (handle->delayed_error)
+    return handle->delayed_error;
+
+  return uv__getsockpeername((const uv_handle_t*) handle,
+                             getsockname,
+                             name,
+                             namelen);
+}
+
+
+int uv_tcp_getpeername(const uv_tcp_t* handle,
+                       struct sockaddr* name,
+                       int* namelen) {
+
+  if (handle->delayed_error)
+    return handle->delayed_error;
+
+  return uv__getsockpeername((const uv_handle_t*) handle,
+                             getpeername,
+                             name,
+                             namelen);
+}
+
+
+int uv_tcp_close_reset(uv_tcp_t* handle, uv_close_cb close_cb) {
+  int fd;
+  struct linger l = { 1, 0 };
+
+  /* Disallow setting SO_LINGER to zero due to some platform inconsistencies */
+  if (handle->flags & UV_HANDLE_SHUTTING)
+    return UV_EINVAL;
+
+  fd = uv__stream_fd(handle);
+  if (0 != setsockopt(fd, SOL_SOCKET, SO_LINGER, &l, sizeof(l))) {
+    if (errno == EINVAL) {
+      /* Open Group Specifications Issue 7, 2018 edition states that
+       * EINVAL may mean the socket has been shut down already.
+       * Behavior observed on Solaris, illumos and macOS. */
+      errno = 0;
+    } else {
+      return UV__ERR(errno);
+    }
+  }
+
+  uv_close((uv_handle_t*) handle, close_cb);
+  return 0;
+}
+
+
+int uv__tcp_listen(uv_tcp_t* tcp, int backlog, uv_connection_cb cb) {
+  static int single_accept_cached = -1;
+  unsigned long flags;
+  int single_accept;
+  int err;
+
+  if (tcp->delayed_error)
+    return tcp->delayed_error;
+
+  single_accept = uv__load_relaxed(&single_accept_cached);
+  if (single_accept == -1) {
+    const char* val = getenv("UV_TCP_SINGLE_ACCEPT");
+    single_accept = (val != NULL && atoi(val) != 0);  /* Off by default. */
+    uv__store_relaxed(&single_accept_cached, single_accept);
+  }
+
+  if (single_accept)
+    tcp->flags |= UV_HANDLE_TCP_SINGLE_ACCEPT;
+
+  flags = 0;
+#if defined(__MVS__)
+  /* on zOS the listen call does not bind automatically
+     if the socket is unbound. Hence the manual binding to
+     an arbitrary port is required to be done manually
+  */
+  flags |= UV_HANDLE_BOUND;
+#endif
+  err = maybe_new_socket(tcp, AF_INET, flags);
+  if (err)
+    return err;
+
+  if (listen(tcp->io_watcher.fd, backlog))
+    return UV__ERR(errno);
+
+  tcp->connection_cb = cb;
+  tcp->flags |= UV_HANDLE_BOUND;
+
+  /* Start listening for connections. */
+  tcp->io_watcher.cb = uv__server_io;
+  uv__io_start(tcp->loop, &tcp->io_watcher, POLLIN);
+
+  return 0;
+}
+
+
+int uv__tcp_nodelay(int fd, int on) {
+  if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on)))
+    return UV__ERR(errno);
+  return 0;
+}
+
+
+int uv__tcp_keepalive(int fd, int on, unsigned int delay) {
+  if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof(on)))
+    return UV__ERR(errno);
+
+#ifdef TCP_KEEPIDLE
+  if (on) {
+    int intvl = 1;  /*  1 second; same as default on Win32 */
+    int cnt = 10;  /* 10 retries; same as hardcoded on Win32 */
+    if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, &delay, sizeof(delay)))
+      return UV__ERR(errno);
+    if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL, &intvl, sizeof(intvl)))
+      return UV__ERR(errno);
+    if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPCNT, &cnt, sizeof(cnt)))
+      return UV__ERR(errno);
+  }
+#endif
+
+  /* Solaris/SmartOS, if you don't support keep-alive,
+   * then don't advertise it in your system headers...
+   */
+  /* FIXME(bnoordhuis) That's possibly because sizeof(delay) should be 1. */
+#if defined(TCP_KEEPALIVE) && !defined(__sun)
+  if (on && setsockopt(fd, IPPROTO_TCP, TCP_KEEPALIVE, &delay, sizeof(delay)))
+    return UV__ERR(errno);
+#endif
+
+  return 0;
+}
+
+
+int uv_tcp_nodelay(uv_tcp_t* handle, int on) {
+  int err;
+
+  if (uv__stream_fd(handle) != -1) {
+    err = uv__tcp_nodelay(uv__stream_fd(handle), on);
+    if (err)
+      return err;
+  }
+
+  if (on)
+    handle->flags |= UV_HANDLE_TCP_NODELAY;
+  else
+    handle->flags &= ~UV_HANDLE_TCP_NODELAY;
+
+  return 0;
+}
+
+
+int uv_tcp_keepalive(uv_tcp_t* handle, int on, unsigned int delay) {
+  int err;
+
+  if (uv__stream_fd(handle) != -1) {
+    err =uv__tcp_keepalive(uv__stream_fd(handle), on, delay);
+    if (err)
+      return err;
+  }
+
+  if (on)
+    handle->flags |= UV_HANDLE_TCP_KEEPALIVE;
+  else
+    handle->flags &= ~UV_HANDLE_TCP_KEEPALIVE;
+
+  /* TODO Store delay if uv__stream_fd(handle) == -1 but don't want to enlarge
+   *      uv_tcp_t with an int that's almost never used...
+   */
+
+  return 0;
+}
+
+
+int uv_tcp_simultaneous_accepts(uv_tcp_t* handle, int enable) {
+  if (enable)
+    handle->flags &= ~UV_HANDLE_TCP_SINGLE_ACCEPT;
+  else
+    handle->flags |= UV_HANDLE_TCP_SINGLE_ACCEPT;
+  return 0;
+}
+
+
+void uv__tcp_close(uv_tcp_t* handle) {
+  uv__stream_close((uv_stream_t*)handle);
+}
+
+
+int uv_socketpair(int type, int protocol, uv_os_sock_t fds[2], int flags0, int flags1) {
+  uv_os_sock_t temp[2];
+  int err;
+#if defined(__FreeBSD__) || defined(__linux__)
+  int flags;
+
+  flags = type | SOCK_CLOEXEC;
+  if ((flags0 & UV_NONBLOCK_PIPE) && (flags1 & UV_NONBLOCK_PIPE))
+    flags |= SOCK_NONBLOCK;
+
+  if (socketpair(AF_UNIX, flags, protocol, temp))
+    return UV__ERR(errno);
+
+  if (flags & UV_FS_O_NONBLOCK) {
+    fds[0] = temp[0];
+    fds[1] = temp[1];
+    return 0;
+  }
+#else
+  if (socketpair(AF_UNIX, type, protocol, temp))
+    return UV__ERR(errno);
+
+  if ((err = uv__cloexec(temp[0], 1)))
+    goto fail;
+  if ((err = uv__cloexec(temp[1], 1)))
+    goto fail;
+#endif
+
+  if (flags0 & UV_NONBLOCK_PIPE)
+    if ((err = uv__nonblock(temp[0], 1)))
+        goto fail;
+  if (flags1 & UV_NONBLOCK_PIPE)
+    if ((err = uv__nonblock(temp[1], 1)))
+      goto fail;
+
+  fds[0] = temp[0];
+  fds[1] = temp[1];
+  return 0;
+
+fail:
+  uv__close(temp[0]);
+  uv__close(temp[1]);
+  return err;
+}
diff --git a/wpinet/src/main/native/thirdparty/libuv/src/unix/thread.cpp b/wpinet/src/main/native/thirdparty/libuv/src/unix/thread.cpp
new file mode 100644
index 0000000..392a071
--- /dev/null
+++ b/wpinet/src/main/native/thirdparty/libuv/src/unix/thread.cpp
@@ -0,0 +1,854 @@
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include "uv.h"
+#include "internal.h"
+
+#include <pthread.h>
+#include <assert.h>
+#include <errno.h>
+
+#include <sys/time.h>
+#include <sys/resource.h>  /* getrlimit() */
+#include <unistd.h>  /* getpagesize() */
+
+#include <limits.h>
+
+#ifdef __MVS__
+#include <sys/ipc.h>
+#include <sys/sem.h>
+#endif
+
+#if defined(__GLIBC__) && !defined(__UCLIBC__)
+#include <gnu/libc-version.h>  /* gnu_get_libc_version() */
+#endif
+
+#undef NANOSEC
+#define NANOSEC ((uint64_t) 1e9)
+
+#if defined(PTHREAD_BARRIER_SERIAL_THREAD)
+STATIC_ASSERT(sizeof(uv_barrier_t) == sizeof(pthread_barrier_t));
+#endif
+
+/* Note: guard clauses should match uv_barrier_t's in include/uv/unix.h. */
+#if defined(_AIX) || \
+    defined(__OpenBSD__) || \
+    !defined(PTHREAD_BARRIER_SERIAL_THREAD)
+int uv_barrier_init(uv_barrier_t* barrier, unsigned int count) {
+  struct _uv_barrier* b;
+  int rc;
+
+  if (barrier == NULL || count == 0)
+    return UV_EINVAL;
+
+  b = (_uv_barrier*)uv__malloc(sizeof(*b));
+  if (b == NULL)
+    return UV_ENOMEM;
+
+  b->in = 0;
+  b->out = 0;
+  b->threshold = count;
+
+  rc = uv_mutex_init(&b->mutex);
+  if (rc != 0)
+    goto error2;
+
+  rc = uv_cond_init(&b->cond);
+  if (rc != 0)
+    goto error;
+
+  barrier->b = b;
+  return 0;
+
+error:
+  uv_mutex_destroy(&b->mutex);
+error2:
+  uv__free(b);
+  return rc;
+}
+
+int uv_barrier_wait(uv_barrier_t* barrier) {
+  struct _uv_barrier* b;
+  int last;
+
+  if (barrier == NULL || barrier->b == NULL)
+    return UV_EINVAL;
+
+  b = barrier->b;
+  /* Lock the mutex*/
+  uv_mutex_lock(&b->mutex);
+
+  if (++b->in == b->threshold) {
+    b->in = 0;
+    b->out = b->threshold;
+    uv_cond_signal(&b->cond);
+  } else {
+    do
+      uv_cond_wait(&b->cond, &b->mutex);
+    while (b->in != 0);
+  }
+
+  last = (--b->out == 0);
+  uv_cond_signal(&b->cond);
+
+  uv_mutex_unlock(&b->mutex);
+  return last;
+}
+
+void uv_barrier_destroy(uv_barrier_t* barrier) {
+  struct _uv_barrier* b;
+
+  b = barrier->b;
+  uv_mutex_lock(&b->mutex);
+
+  assert(b->in == 0);
+  while (b->out != 0)
+    uv_cond_wait(&b->cond, &b->mutex);
+
+  if (b->in != 0)
+    abort();
+
+  uv_mutex_unlock(&b->mutex);
+  uv_mutex_destroy(&b->mutex);
+  uv_cond_destroy(&b->cond);
+
+  uv__free(barrier->b);
+  barrier->b = NULL;
+}
+
+#else
+
+int uv_barrier_init(uv_barrier_t* barrier, unsigned int count) {
+  return UV__ERR(pthread_barrier_init(barrier, NULL, count));
+}
+
+
+int uv_barrier_wait(uv_barrier_t* barrier) {
+  int rc;
+
+  rc = pthread_barrier_wait(barrier);
+  if (rc != 0)
+    if (rc != PTHREAD_BARRIER_SERIAL_THREAD)
+      abort();
+
+  return rc == PTHREAD_BARRIER_SERIAL_THREAD;
+}
+
+
+void uv_barrier_destroy(uv_barrier_t* barrier) {
+  if (pthread_barrier_destroy(barrier))
+    abort();
+}
+
+#endif
+
+
+/* Musl's PTHREAD_STACK_MIN is 2 KB on all architectures, which is
+ * too small to safely receive signals on.
+ *
+ * Musl's PTHREAD_STACK_MIN + MINSIGSTKSZ == 8192 on arm64 (which has
+ * the largest MINSIGSTKSZ of the architectures that musl supports) so
+ * let's use that as a lower bound.
+ *
+ * We use a hardcoded value because PTHREAD_STACK_MIN + MINSIGSTKSZ
+ * is between 28 and 133 KB when compiling against glibc, depending
+ * on the architecture.
+ */
+static size_t uv__min_stack_size(void) {
+  static const size_t min = 8192;
+
+#ifdef PTHREAD_STACK_MIN  /* Not defined on NetBSD. */
+  if (min < (size_t) PTHREAD_STACK_MIN)
+    return PTHREAD_STACK_MIN;
+#endif  /* PTHREAD_STACK_MIN */
+
+  return min;
+}
+
+
+/* On Linux, threads created by musl have a much smaller stack than threads
+ * created by glibc (80 vs. 2048 or 4096 kB.)  Follow glibc for consistency.
+ */
+static size_t uv__default_stack_size(void) {
+#if !defined(__linux__)
+  return 0;
+#elif defined(__PPC__) || defined(__ppc__) || defined(__powerpc__)
+  return 4 << 20;  /* glibc default. */
+#else
+  return 2 << 20;  /* glibc default. */
+#endif
+}
+
+
+/* On MacOS, threads other than the main thread are created with a reduced
+ * stack size by default.  Adjust to RLIMIT_STACK aligned to the page size.
+ */
+size_t uv__thread_stack_size(void) {
+#if defined(__APPLE__) || defined(__linux__)
+  struct rlimit lim;
+
+  /* getrlimit() can fail on some aarch64 systems due to a glibc bug where
+   * the system call wrapper invokes the wrong system call. Don't treat
+   * that as fatal, just use the default stack size instead.
+   */
+  if (getrlimit(RLIMIT_STACK, &lim))
+    return uv__default_stack_size();
+
+  if (lim.rlim_cur == RLIM_INFINITY)
+    return uv__default_stack_size();
+
+  /* pthread_attr_setstacksize() expects page-aligned values. */
+  lim.rlim_cur -= lim.rlim_cur % (rlim_t) getpagesize();
+
+  if (lim.rlim_cur >= (rlim_t) uv__min_stack_size())
+    return lim.rlim_cur;
+#endif
+
+  return uv__default_stack_size();
+}
+
+
+int uv_thread_create(uv_thread_t *tid, void (*entry)(void *arg), void *arg) {
+  uv_thread_options_t params;
+  params.flags = UV_THREAD_NO_FLAGS;
+  return uv_thread_create_ex(tid, &params, entry, arg);
+}
+
+int uv_thread_create_ex(uv_thread_t* tid,
+                        const uv_thread_options_t* params,
+                        void (*entry)(void *arg),
+                        void *arg) {
+  int err;
+  pthread_attr_t* attr;
+  pthread_attr_t attr_storage;
+  size_t pagesize;
+  size_t stack_size;
+  size_t min_stack_size;
+
+  stack_size =
+      params->flags & UV_THREAD_HAS_STACK_SIZE ? params->stack_size : 0;
+
+  attr = NULL;
+  if (stack_size == 0) {
+    stack_size = uv__thread_stack_size();
+  } else {
+    pagesize = (size_t)getpagesize();
+    /* Round up to the nearest page boundary. */
+    stack_size = (stack_size + pagesize - 1) &~ (pagesize - 1);
+    min_stack_size = uv__min_stack_size();
+    if (stack_size < min_stack_size)
+      stack_size = min_stack_size;
+  }
+
+  if (stack_size > 0) {
+    attr = &attr_storage;
+
+    if (pthread_attr_init(attr))
+      abort();
+
+    if (pthread_attr_setstacksize(attr, stack_size))
+      abort();
+  }
+
+  err = pthread_create(tid, attr, (void*(*)(void*)) (void(*)(void)) entry, arg);
+
+  if (attr != NULL)
+    pthread_attr_destroy(attr);
+
+  return UV__ERR(err);
+}
+
+
+uv_thread_t uv_thread_self(void) {
+  return pthread_self();
+}
+
+int uv_thread_join(uv_thread_t *tid) {
+  return UV__ERR(pthread_join(*tid, NULL));
+}
+
+
+int uv_thread_equal(const uv_thread_t* t1, const uv_thread_t* t2) {
+  return pthread_equal(*t1, *t2);
+}
+
+
+int uv_mutex_init(uv_mutex_t* mutex) {
+#if defined(NDEBUG) || !defined(PTHREAD_MUTEX_ERRORCHECK)
+  return UV__ERR(pthread_mutex_init(mutex, NULL));
+#else
+  pthread_mutexattr_t attr;
+  int err;
+
+  if (pthread_mutexattr_init(&attr))
+    abort();
+
+  if (pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK))
+    abort();
+
+  err = pthread_mutex_init(mutex, &attr);
+
+  if (pthread_mutexattr_destroy(&attr))
+    abort();
+
+  return UV__ERR(err);
+#endif
+}
+
+
+int uv_mutex_init_recursive(uv_mutex_t* mutex) {
+  pthread_mutexattr_t attr;
+  int err;
+
+  if (pthread_mutexattr_init(&attr))
+    abort();
+
+  if (pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE))
+    abort();
+
+  err = pthread_mutex_init(mutex, &attr);
+
+  if (pthread_mutexattr_destroy(&attr))
+    abort();
+
+  return UV__ERR(err);
+}
+
+
+void uv_mutex_destroy(uv_mutex_t* mutex) {
+  if (pthread_mutex_destroy(mutex))
+    abort();
+}
+
+
+void uv_mutex_lock(uv_mutex_t* mutex) {
+  if (pthread_mutex_lock(mutex))
+    abort();
+}
+
+
+int uv_mutex_trylock(uv_mutex_t* mutex) {
+  int err;
+
+  err = pthread_mutex_trylock(mutex);
+  if (err) {
+    if (err != EBUSY && err != EAGAIN)
+      abort();
+    return UV_EBUSY;
+  }
+
+  return 0;
+}
+
+
+void uv_mutex_unlock(uv_mutex_t* mutex) {
+  if (pthread_mutex_unlock(mutex))
+    abort();
+}
+
+
+int uv_rwlock_init(uv_rwlock_t* rwlock) {
+  return UV__ERR(pthread_rwlock_init(rwlock, NULL));
+}
+
+
+void uv_rwlock_destroy(uv_rwlock_t* rwlock) {
+  if (pthread_rwlock_destroy(rwlock))
+    abort();
+}
+
+
+void uv_rwlock_rdlock(uv_rwlock_t* rwlock) {
+  if (pthread_rwlock_rdlock(rwlock))
+    abort();
+}
+
+
+int uv_rwlock_tryrdlock(uv_rwlock_t* rwlock) {
+  int err;
+
+  err = pthread_rwlock_tryrdlock(rwlock);
+  if (err) {
+    if (err != EBUSY && err != EAGAIN)
+      abort();
+    return UV_EBUSY;
+  }
+
+  return 0;
+}
+
+
+void uv_rwlock_rdunlock(uv_rwlock_t* rwlock) {
+  if (pthread_rwlock_unlock(rwlock))
+    abort();
+}
+
+
+void uv_rwlock_wrlock(uv_rwlock_t* rwlock) {
+  if (pthread_rwlock_wrlock(rwlock))
+    abort();
+}
+
+
+int uv_rwlock_trywrlock(uv_rwlock_t* rwlock) {
+  int err;
+
+  err = pthread_rwlock_trywrlock(rwlock);
+  if (err) {
+    if (err != EBUSY && err != EAGAIN)
+      abort();
+    return UV_EBUSY;
+  }
+
+  return 0;
+}
+
+
+void uv_rwlock_wrunlock(uv_rwlock_t* rwlock) {
+  if (pthread_rwlock_unlock(rwlock))
+    abort();
+}
+
+
+void uv_once(uv_once_t* guard, void (*callback)(void)) {
+  if (pthread_once(guard, callback))
+    abort();
+}
+
+#if defined(__APPLE__) && defined(__MACH__)
+
+int uv_sem_init(uv_sem_t* sem, unsigned int value) {
+  kern_return_t err;
+
+  err = semaphore_create(mach_task_self(), sem, SYNC_POLICY_FIFO, value);
+  if (err == KERN_SUCCESS)
+    return 0;
+  if (err == KERN_INVALID_ARGUMENT)
+    return UV_EINVAL;
+  if (err == KERN_RESOURCE_SHORTAGE)
+    return UV_ENOMEM;
+
+  abort();
+  return UV_EINVAL;  /* Satisfy the compiler. */
+}
+
+
+void uv_sem_destroy(uv_sem_t* sem) {
+  if (semaphore_destroy(mach_task_self(), *sem))
+    abort();
+}
+
+
+void uv_sem_post(uv_sem_t* sem) {
+  if (semaphore_signal(*sem))
+    abort();
+}
+
+
+void uv_sem_wait(uv_sem_t* sem) {
+  int r;
+
+  do
+    r = semaphore_wait(*sem);
+  while (r == KERN_ABORTED);
+
+  if (r != KERN_SUCCESS)
+    abort();
+}
+
+
+int uv_sem_trywait(uv_sem_t* sem) {
+  mach_timespec_t interval;
+  kern_return_t err;
+
+  interval.tv_sec = 0;
+  interval.tv_nsec = 0;
+
+  err = semaphore_timedwait(*sem, interval);
+  if (err == KERN_SUCCESS)
+    return 0;
+  if (err == KERN_OPERATION_TIMED_OUT)
+    return UV_EAGAIN;
+
+  abort();
+  return UV_EINVAL;  /* Satisfy the compiler. */
+}
+
+#else /* !(defined(__APPLE__) && defined(__MACH__)) */
+
+#if defined(__GLIBC__) && !defined(__UCLIBC__)
+
+/* Hack around https://sourceware.org/bugzilla/show_bug.cgi?id=12674
+ * by providing a custom implementation for glibc < 2.21 in terms of other
+ * concurrency primitives.
+ * Refs: https://github.com/nodejs/node/issues/19903 */
+
+/* To preserve ABI compatibility, we treat the uv_sem_t as storage for
+ * a pointer to the actual struct we're using underneath. */
+
+static uv_once_t glibc_version_check_once = UV_ONCE_INIT;
+static int platform_needs_custom_semaphore = 0;
+
+static void glibc_version_check(void) {
+  const char* version = gnu_get_libc_version();
+  platform_needs_custom_semaphore =
+      version[0] == '2' && version[1] == '.' &&
+      atoi(version + 2) < 21;
+}
+
+#elif defined(__MVS__)
+
+#define platform_needs_custom_semaphore 1
+
+#else /* !defined(__GLIBC__) && !defined(__MVS__) */
+
+#define platform_needs_custom_semaphore 0
+
+#endif
+
+typedef struct uv_semaphore_s {
+  uv_mutex_t mutex;
+  uv_cond_t cond;
+  unsigned int value;
+} uv_semaphore_t;
+
+#if (defined(__GLIBC__) && !defined(__UCLIBC__)) || \
+    platform_needs_custom_semaphore
+STATIC_ASSERT(sizeof(uv_sem_t) >= sizeof(uv_semaphore_t*));
+#endif
+
+static int uv__custom_sem_init(uv_sem_t* sem_, unsigned int value) {
+  int err;
+  uv_semaphore_t* sem;
+
+  sem = (uv_semaphore_t*)uv__malloc(sizeof(*sem));
+  if (sem == NULL)
+    return UV_ENOMEM;
+
+  if ((err = uv_mutex_init(&sem->mutex)) != 0) {
+    uv__free(sem);
+    return err;
+  }
+
+  if ((err = uv_cond_init(&sem->cond)) != 0) {
+    uv_mutex_destroy(&sem->mutex);
+    uv__free(sem);
+    return err;
+  }
+
+  sem->value = value;
+  *(uv_semaphore_t**)sem_ = sem;
+  return 0;
+}
+
+
+static void uv__custom_sem_destroy(uv_sem_t* sem_) {
+  uv_semaphore_t* sem;
+
+  sem = *(uv_semaphore_t**)sem_;
+  uv_cond_destroy(&sem->cond);
+  uv_mutex_destroy(&sem->mutex);
+  uv__free(sem);
+}
+
+
+static void uv__custom_sem_post(uv_sem_t* sem_) {
+  uv_semaphore_t* sem;
+
+  sem = *(uv_semaphore_t**)sem_;
+  uv_mutex_lock(&sem->mutex);
+  sem->value++;
+  if (sem->value == 1)
+    uv_cond_signal(&sem->cond);
+  uv_mutex_unlock(&sem->mutex);
+}
+
+
+static void uv__custom_sem_wait(uv_sem_t* sem_) {
+  uv_semaphore_t* sem;
+
+  sem = *(uv_semaphore_t**)sem_;
+  uv_mutex_lock(&sem->mutex);
+  while (sem->value == 0)
+    uv_cond_wait(&sem->cond, &sem->mutex);
+  sem->value--;
+  uv_mutex_unlock(&sem->mutex);
+}
+
+
+static int uv__custom_sem_trywait(uv_sem_t* sem_) {
+  uv_semaphore_t* sem;
+
+  sem = *(uv_semaphore_t**)sem_;
+  if (uv_mutex_trylock(&sem->mutex) != 0)
+    return UV_EAGAIN;
+
+  if (sem->value == 0) {
+    uv_mutex_unlock(&sem->mutex);
+    return UV_EAGAIN;
+  }
+
+  sem->value--;
+  uv_mutex_unlock(&sem->mutex);
+
+  return 0;
+}
+
+static int uv__sem_init(uv_sem_t* sem, unsigned int value) {
+  if (sem_init(sem, 0, value))
+    return UV__ERR(errno);
+  return 0;
+}
+
+
+static void uv__sem_destroy(uv_sem_t* sem) {
+  if (sem_destroy(sem))
+    abort();
+}
+
+
+static void uv__sem_post(uv_sem_t* sem) {
+  if (sem_post(sem))
+    abort();
+}
+
+
+static void uv__sem_wait(uv_sem_t* sem) {
+  int r;
+
+  do
+    r = sem_wait(sem);
+  while (r == -1 && errno == EINTR);
+
+  if (r)
+    abort();
+}
+
+
+static int uv__sem_trywait(uv_sem_t* sem) {
+  int r;
+
+  do
+    r = sem_trywait(sem);
+  while (r == -1 && errno == EINTR);
+
+  if (r) {
+    if (errno == EAGAIN)
+      return UV_EAGAIN;
+    abort();
+  }
+
+  return 0;
+}
+
+int uv_sem_init(uv_sem_t* sem, unsigned int value) {
+#if defined(__GLIBC__) && !defined(__UCLIBC__)
+  uv_once(&glibc_version_check_once, glibc_version_check);
+#endif
+
+  if (platform_needs_custom_semaphore)
+    return uv__custom_sem_init(sem, value);
+  else
+    return uv__sem_init(sem, value);
+}
+
+
+void uv_sem_destroy(uv_sem_t* sem) {
+  if (platform_needs_custom_semaphore)
+    uv__custom_sem_destroy(sem);
+  else
+    uv__sem_destroy(sem);
+}
+
+
+void uv_sem_post(uv_sem_t* sem) {
+  if (platform_needs_custom_semaphore)
+    uv__custom_sem_post(sem);
+  else
+    uv__sem_post(sem);
+}
+
+
+void uv_sem_wait(uv_sem_t* sem) {
+  if (platform_needs_custom_semaphore)
+    uv__custom_sem_wait(sem);
+  else
+    uv__sem_wait(sem);
+}
+
+
+int uv_sem_trywait(uv_sem_t* sem) {
+  if (platform_needs_custom_semaphore)
+    return uv__custom_sem_trywait(sem);
+  else
+    return uv__sem_trywait(sem);
+}
+
+#endif /* defined(__APPLE__) && defined(__MACH__) */
+
+
+#if defined(__APPLE__) && defined(__MACH__) || defined(__MVS__)
+
+int uv_cond_init(uv_cond_t* cond) {
+  return UV__ERR(pthread_cond_init(cond, NULL));
+}
+
+#else /* !(defined(__APPLE__) && defined(__MACH__)) */
+
+int uv_cond_init(uv_cond_t* cond) {
+  pthread_condattr_t attr;
+  int err;
+
+  err = pthread_condattr_init(&attr);
+  if (err)
+    return UV__ERR(err);
+
+  err = pthread_condattr_setclock(&attr, CLOCK_MONOTONIC);
+  if (err)
+    goto error2;
+
+  err = pthread_cond_init(cond, &attr);
+  if (err)
+    goto error2;
+
+  err = pthread_condattr_destroy(&attr);
+  if (err)
+    goto error;
+
+  return 0;
+
+error:
+  pthread_cond_destroy(cond);
+error2:
+  pthread_condattr_destroy(&attr);
+  return UV__ERR(err);
+}
+
+#endif /* defined(__APPLE__) && defined(__MACH__) */
+
+void uv_cond_destroy(uv_cond_t* cond) {
+#if defined(__APPLE__) && defined(__MACH__)
+  /* It has been reported that destroying condition variables that have been
+   * signalled but not waited on can sometimes result in application crashes.
+   * See https://codereview.chromium.org/1323293005.
+   */
+  pthread_mutex_t mutex;
+  struct timespec ts;
+  int err;
+
+  if (pthread_mutex_init(&mutex, NULL))
+    abort();
+
+  if (pthread_mutex_lock(&mutex))
+    abort();
+
+  ts.tv_sec = 0;
+  ts.tv_nsec = 1;
+
+  err = pthread_cond_timedwait_relative_np(cond, &mutex, &ts);
+  if (err != 0 && err != ETIMEDOUT)
+    abort();
+
+  if (pthread_mutex_unlock(&mutex))
+    abort();
+
+  if (pthread_mutex_destroy(&mutex))
+    abort();
+#endif /* defined(__APPLE__) && defined(__MACH__) */
+
+  if (pthread_cond_destroy(cond))
+    abort();
+}
+
+void uv_cond_signal(uv_cond_t* cond) {
+  if (pthread_cond_signal(cond))
+    abort();
+}
+
+void uv_cond_broadcast(uv_cond_t* cond) {
+  if (pthread_cond_broadcast(cond))
+    abort();
+}
+
+void uv_cond_wait(uv_cond_t* cond, uv_mutex_t* mutex) {
+  if (pthread_cond_wait(cond, mutex))
+    abort();
+}
+
+
+int uv_cond_timedwait(uv_cond_t* cond, uv_mutex_t* mutex, uint64_t timeout) {
+  int r;
+  struct timespec ts;
+#if defined(__MVS__)
+  struct timeval tv;
+#endif
+
+#if defined(__APPLE__) && defined(__MACH__)
+  ts.tv_sec = timeout / NANOSEC;
+  ts.tv_nsec = timeout % NANOSEC;
+  r = pthread_cond_timedwait_relative_np(cond, mutex, &ts);
+#else
+#if defined(__MVS__)
+  if (gettimeofday(&tv, NULL))
+    abort();
+  timeout += tv.tv_sec * NANOSEC + tv.tv_usec * 1e3;
+#else
+  timeout += uv__hrtime(UV_CLOCK_PRECISE);
+#endif
+  ts.tv_sec = timeout / NANOSEC;
+  ts.tv_nsec = timeout % NANOSEC;
+  r = pthread_cond_timedwait(cond, mutex, &ts);
+#endif
+
+
+  if (r == 0)
+    return 0;
+
+  if (r == ETIMEDOUT)
+    return UV_ETIMEDOUT;
+
+  abort();
+#ifndef __SUNPRO_C
+  return UV_EINVAL;  /* Satisfy the compiler. */
+#endif
+}
+
+
+int uv_key_create(uv_key_t* key) {
+  return UV__ERR(pthread_key_create(key, NULL));
+}
+
+
+void uv_key_delete(uv_key_t* key) {
+  if (pthread_key_delete(*key))
+    abort();
+}
+
+
+void* uv_key_get(uv_key_t* key) {
+  return pthread_getspecific(*key);
+}
+
+
+void uv_key_set(uv_key_t* key, void* value) {
+  if (pthread_setspecific(*key, value))
+    abort();
+}
diff --git a/wpinet/src/main/native/thirdparty/libuv/src/unix/tty.cpp b/wpinet/src/main/native/thirdparty/libuv/src/unix/tty.cpp
new file mode 100644
index 0000000..ed81e26
--- /dev/null
+++ b/wpinet/src/main/native/thirdparty/libuv/src/unix/tty.cpp
@@ -0,0 +1,467 @@
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include "uv.h"
+#include "internal.h"
+#include "spinlock.h"
+
+#include <stdlib.h>
+#include <assert.h>
+#include <unistd.h>
+#include <termios.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+
+#if defined(__MVS__) && !defined(IMAXBEL)
+#define IMAXBEL 0
+#endif
+
+#if defined(__PASE__)
+/* On IBM i PASE, for better compatibility with running interactive programs in
+ * a 5250 environment, isatty() will return true for the stdin/stdout/stderr
+ * streams created by QSH/QP2TERM.
+ *
+ * For more, see docs on PASE_STDIO_ISATTY in
+ * https://www.ibm.com/support/knowledgecenter/ssw_ibm_i_74/apis/pase_environ.htm
+ *
+ * This behavior causes problems for Node as it expects that if isatty() returns
+ * true that TTY ioctls will be supported by that fd (which is not an
+ * unreasonable expectation) and when they don't it crashes with assertion
+ * errors.
+ *
+ * Here, we create our own version of isatty() that uses ioctl() to identify
+ * whether the fd is *really* a TTY or not.
+ */
+static int isreallyatty(int file) {
+  int rc;
+ 
+  rc = !ioctl(file, TXISATTY + 0x81, NULL);
+  if (!rc && errno != EBADF)
+      errno = ENOTTY;
+
+  return rc;
+}
+#define isatty(fd) isreallyatty(fd)
+#endif
+
+static int orig_termios_fd = -1;
+static struct termios orig_termios;
+static uv_spinlock_t termios_spinlock = UV_SPINLOCK_INITIALIZER;
+
+int uv__tcsetattr(int fd, int how, const struct termios *term) {
+  int rc;
+
+  do
+    rc = tcsetattr(fd, how, term);
+  while (rc == -1 && errno == EINTR);
+
+  if (rc == -1)
+    return UV__ERR(errno);
+
+  return 0;
+}
+
+static int uv__tty_is_peripheral(const int fd) {
+  int result;
+#if defined(__linux__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
+  int dummy;
+
+  result = ioctl(fd, TIOCGPTN, &dummy) != 0;
+#elif defined(__APPLE__)
+  char dummy[256];
+
+  result = ioctl(fd, TIOCPTYGNAME, &dummy) != 0;
+#elif defined(__NetBSD__)
+  /*
+   * NetBSD as an extension returns with ptsname(3) and ptsname_r(3) the
+   * peripheral device name for both descriptors, the controller one and
+   * peripheral one.
+   *
+   * Implement function to compare major device number with pts devices.
+   *
+   * The major numbers are machine-dependent, on NetBSD/amd64 they are
+   * respectively:
+   *  - controller tty: ptc - major 6
+   *  - peripheral tty:  pts - major 5
+   */
+
+  struct stat sb;
+  /* Lookup device's major for the pts driver and cache it. */
+  static devmajor_t pts = NODEVMAJOR;
+
+  if (pts == NODEVMAJOR) {
+    pts = getdevmajor("pts", S_IFCHR);
+    if (pts == NODEVMAJOR)
+      abort();
+  }
+
+  /* Lookup stat structure behind the file descriptor. */
+  if (fstat(fd, &sb) != 0)
+    abort();
+
+  /* Assert character device. */
+  if (!S_ISCHR(sb.st_mode))
+    abort();
+
+  /* Assert valid major. */
+  if (major(sb.st_rdev) == NODEVMAJOR)
+    abort();
+
+  result = (pts == major(sb.st_rdev));
+#else
+  /* Fallback to ptsname
+   */
+  result = ptsname(fd) == NULL;
+#endif
+  return result;
+}
+
+int uv_tty_init(uv_loop_t* loop, uv_tty_t* tty, int fd, int unused) {
+  uv_handle_type type;
+  int flags;
+  int newfd;
+  int r;
+  int saved_flags;
+  int mode;
+  char path[256];
+  (void)unused; /* deprecated parameter is no longer needed */
+
+  /* File descriptors that refer to files cannot be monitored with epoll.
+   * That restriction also applies to character devices like /dev/random
+   * (but obviously not /dev/tty.)
+   */
+  type = uv_guess_handle(fd);
+  if (type == UV_FILE || type == UV_UNKNOWN_HANDLE)
+    return UV_EINVAL;
+
+  flags = 0;
+  newfd = -1;
+
+  /* Save the fd flags in case we need to restore them due to an error. */
+  do
+    saved_flags = fcntl(fd, F_GETFL);
+  while (saved_flags == -1 && errno == EINTR);
+
+  if (saved_flags == -1)
+    return UV__ERR(errno);
+  mode = saved_flags & O_ACCMODE;
+
+  /* Reopen the file descriptor when it refers to a tty. This lets us put the
+   * tty in non-blocking mode without affecting other processes that share it
+   * with us.
+   *
+   * Example: `node | cat` - if we put our fd 0 in non-blocking mode, it also
+   * affects fd 1 of `cat` because both file descriptors refer to the same
+   * struct file in the kernel. When we reopen our fd 0, it points to a
+   * different struct file, hence changing its properties doesn't affect
+   * other processes.
+   */
+  if (type == UV_TTY) {
+    /* Reopening a pty in controller mode won't work either because the reopened
+     * pty will be in peripheral mode (*BSD) or reopening will allocate a new
+     * controller/peripheral pair (Linux). Therefore check if the fd points to a
+     * peripheral device.
+     */
+    if (uv__tty_is_peripheral(fd) && ttyname_r(fd, path, sizeof(path)) == 0)
+      r = uv__open_cloexec(path, mode | O_NOCTTY);
+    else
+      r = -1;
+
+    if (r < 0) {
+      /* fallback to using blocking writes */
+      if (mode != O_RDONLY)
+        flags |= UV_HANDLE_BLOCKING_WRITES;
+      goto skip;
+    }
+
+    newfd = r;
+
+    r = uv__dup2_cloexec(newfd, fd);
+    if (r < 0 && r != UV_EINVAL) {
+      /* EINVAL means newfd == fd which could conceivably happen if another
+       * thread called close(fd) between our calls to isatty() and open().
+       * That's a rather unlikely event but let's handle it anyway.
+       */
+      uv__close(newfd);
+      return r;
+    }
+
+    fd = newfd;
+  }
+
+skip:
+  uv__stream_init(loop, (uv_stream_t*) tty, UV_TTY);
+
+  /* If anything fails beyond this point we need to remove the handle from
+   * the handle queue, since it was added by uv__handle_init in uv_stream_init.
+   */
+
+  if (!(flags & UV_HANDLE_BLOCKING_WRITES))
+    uv__nonblock(fd, 1);
+
+#if defined(__APPLE__)
+  r = uv__stream_try_select((uv_stream_t*) tty, &fd);
+  if (r) {
+    int rc = r;
+    if (newfd != -1)
+      uv__close(newfd);
+    QUEUE_REMOVE(&tty->handle_queue);
+    do
+      r = fcntl(fd, F_SETFL, saved_flags);
+    while (r == -1 && errno == EINTR);
+    return rc;
+  }
+#endif
+
+  if (mode != O_WRONLY)
+    flags |= UV_HANDLE_READABLE;
+  if (mode != O_RDONLY)
+    flags |= UV_HANDLE_WRITABLE;
+
+  uv__stream_open((uv_stream_t*) tty, fd, flags);
+  tty->mode = UV_TTY_MODE_NORMAL;
+
+  return 0;
+}
+
+static void uv__tty_make_raw(struct termios* tio) {
+  assert(tio != NULL);
+
+#if defined __sun || defined __MVS__
+  /*
+   * This implementation of cfmakeraw for Solaris and derivatives is taken from
+   * http://www.perkin.org.uk/posts/solaris-portability-cfmakeraw.html.
+   */
+  tio->c_iflag &= ~(IMAXBEL | IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR |
+                    IGNCR | ICRNL | IXON);
+  tio->c_oflag &= ~OPOST;
+  tio->c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
+  tio->c_cflag &= ~(CSIZE | PARENB);
+  tio->c_cflag |= CS8;
+
+  /*
+   * By default, most software expects a pending read to block until at
+   * least one byte becomes available.  As per termio(7I), this requires
+   * setting the MIN and TIME parameters appropriately.
+   *
+   * As a somewhat unfortunate artifact of history, the MIN and TIME slots
+   * in the control character array overlap with the EOF and EOL slots used
+   * for canonical mode processing.  Because the EOF character needs to be
+   * the ASCII EOT value (aka Control-D), it has the byte value 4.  When
+   * switching to raw mode, this is interpreted as a MIN value of 4; i.e.,
+   * reads will block until at least four bytes have been input.
+   *
+   * Other platforms with a distinct MIN slot like Linux and FreeBSD appear
+   * to default to a MIN value of 1, so we'll force that value here:
+   */
+  tio->c_cc[VMIN] = 1;
+  tio->c_cc[VTIME] = 0;
+#else
+  cfmakeraw(tio);
+#endif /* #ifdef __sun */
+}
+
+int uv_tty_set_mode(uv_tty_t* tty, uv_tty_mode_t mode) {
+  struct termios tmp;
+  int fd;
+  int rc;
+
+  if (tty->mode == (int) mode)
+    return 0;
+
+  fd = uv__stream_fd(tty);
+  if (tty->mode == UV_TTY_MODE_NORMAL && mode != UV_TTY_MODE_NORMAL) {
+    do
+      rc = tcgetattr(fd, &tty->orig_termios);
+    while (rc == -1 && errno == EINTR);
+
+    if (rc == -1)
+      return UV__ERR(errno);
+
+    /* This is used for uv_tty_reset_mode() */
+    uv_spinlock_lock(&termios_spinlock);
+    if (orig_termios_fd == -1) {
+      orig_termios = tty->orig_termios;
+      orig_termios_fd = fd;
+    }
+    uv_spinlock_unlock(&termios_spinlock);
+  }
+
+  tmp = tty->orig_termios;
+  switch (mode) {
+    case UV_TTY_MODE_NORMAL:
+      break;
+    case UV_TTY_MODE_RAW:
+      tmp.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
+      tmp.c_oflag |= (ONLCR);
+      tmp.c_cflag |= (CS8);
+      tmp.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
+      tmp.c_cc[VMIN] = 1;
+      tmp.c_cc[VTIME] = 0;
+      break;
+    case UV_TTY_MODE_IO:
+      uv__tty_make_raw(&tmp);
+      break;
+  }
+
+  /* Apply changes after draining */
+  rc = uv__tcsetattr(fd, TCSADRAIN, &tmp);
+  if (rc == 0)
+    tty->mode = mode;
+
+  return rc;
+}
+
+
+int uv_tty_get_winsize(uv_tty_t* tty, int* width, int* height) {
+  struct winsize ws;
+  int err;
+
+  do
+    err = ioctl(uv__stream_fd(tty), TIOCGWINSZ, &ws);
+  while (err == -1 && errno == EINTR);
+
+  if (err == -1)
+    return UV__ERR(errno);
+
+  *width = ws.ws_col;
+  *height = ws.ws_row;
+
+  return 0;
+}
+
+
+uv_handle_type uv_guess_handle(uv_file file) {
+  struct sockaddr_storage ss;
+  struct stat s;
+  socklen_t len;
+  int type;
+
+  if (file < 0)
+    return UV_UNKNOWN_HANDLE;
+
+  if (isatty(file))
+    return UV_TTY;
+
+  if (fstat(file, &s)) {
+#if defined(__PASE__)
+    /* On ibmi receiving RST from TCP instead of FIN immediately puts fd into
+     * an error state. fstat will return EINVAL, getsockname will also return
+     * EINVAL, even if sockaddr_storage is valid. (If file does not refer to a
+     * socket, ENOTSOCK is returned instead.)
+     * In such cases, we will permit the user to open the connection as uv_tcp
+     * still, so that the user can get immediately notified of the error in
+     * their read callback and close this fd.
+     */
+    len = sizeof(ss);
+    if (getsockname(file, (struct sockaddr*) &ss, &len)) {
+      if (errno == EINVAL)
+        return UV_TCP;
+    }
+#endif
+    return UV_UNKNOWN_HANDLE;
+  }
+
+  if (S_ISREG(s.st_mode))
+    return UV_FILE;
+
+  if (S_ISCHR(s.st_mode))
+    return UV_FILE;  /* XXX UV_NAMED_PIPE? */
+
+  if (S_ISFIFO(s.st_mode))
+    return UV_NAMED_PIPE;
+
+  if (!S_ISSOCK(s.st_mode))
+    return UV_UNKNOWN_HANDLE;
+
+  len = sizeof(ss);
+  if (getsockname(file, (struct sockaddr*) &ss, &len)) {
+#if defined(_AIX)
+    /* On aix receiving RST from TCP instead of FIN immediately puts fd into
+     * an error state. In such case getsockname will return EINVAL, even if
+     * sockaddr_storage is valid.
+     * In such cases, we will permit the user to open the connection as uv_tcp
+     * still, so that the user can get immediately notified of the error in
+     * their read callback and close this fd.
+     */
+    if (errno == EINVAL) {
+      return UV_TCP;
+    }
+#endif
+    return UV_UNKNOWN_HANDLE;
+  }
+
+  len = sizeof(type);
+  if (getsockopt(file, SOL_SOCKET, SO_TYPE, &type, &len))
+    return UV_UNKNOWN_HANDLE;
+
+  if (type == SOCK_DGRAM)
+    if (ss.ss_family == AF_INET || ss.ss_family == AF_INET6)
+      return UV_UDP;
+
+  if (type == SOCK_STREAM) {
+#if defined(_AIX) || defined(__DragonFly__)
+    /* on AIX/DragonFly the getsockname call returns an empty sa structure
+     * for sockets of type AF_UNIX.  For all other types it will
+     * return a properly filled in structure.
+     */
+    if (len == 0)
+      return UV_NAMED_PIPE;
+#endif /* defined(_AIX) || defined(__DragonFly__) */
+
+    if (ss.ss_family == AF_INET || ss.ss_family == AF_INET6)
+      return UV_TCP;
+    if (ss.ss_family == AF_UNIX)
+      return UV_NAMED_PIPE;
+  }
+
+  return UV_UNKNOWN_HANDLE;
+}
+
+
+/* This function is async signal-safe, meaning that it's safe to call from
+ * inside a signal handler _unless_ execution was inside uv_tty_set_mode()'s
+ * critical section when the signal was raised.
+ */
+int uv_tty_reset_mode(void) {
+  int saved_errno;
+  int err;
+
+  saved_errno = errno;
+  if (!uv_spinlock_trylock(&termios_spinlock))
+    return UV_EBUSY;  /* In uv_tty_set_mode(). */
+
+  err = 0;
+  if (orig_termios_fd != -1)
+    err = uv__tcsetattr(orig_termios_fd, TCSANOW, &orig_termios);
+
+  uv_spinlock_unlock(&termios_spinlock);
+  errno = saved_errno;
+
+  return err;
+}
+
+void uv_tty_set_vterm_state(uv_tty_vtermstate_t state) {
+}
+
+int uv_tty_get_vterm_state(uv_tty_vtermstate_t* state) {
+  return UV_ENOTSUP;
+}
diff --git a/wpinet/src/main/native/thirdparty/libuv/src/unix/udp.cpp b/wpinet/src/main/native/thirdparty/libuv/src/unix/udp.cpp
new file mode 100644
index 0000000..a130aea
--- /dev/null
+++ b/wpinet/src/main/native/thirdparty/libuv/src/unix/udp.cpp
@@ -0,0 +1,1416 @@
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include "uv.h"
+#include "internal.h"
+
+#include <assert.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <unistd.h>
+#if defined(__MVS__)
+#include <xti.h>
+#endif
+#include <sys/un.h>
+
+#if defined(IPV6_JOIN_GROUP) && !defined(IPV6_ADD_MEMBERSHIP)
+# define IPV6_ADD_MEMBERSHIP IPV6_JOIN_GROUP
+#endif
+
+#if defined(IPV6_LEAVE_GROUP) && !defined(IPV6_DROP_MEMBERSHIP)
+# define IPV6_DROP_MEMBERSHIP IPV6_LEAVE_GROUP
+#endif
+
+union uv__sockaddr {
+  struct sockaddr_in6 in6;
+  struct sockaddr_in in;
+  struct sockaddr addr;
+};
+
+static void uv__udp_run_completed(uv_udp_t* handle);
+static void uv__udp_io(uv_loop_t* loop, uv__io_t* w, unsigned int revents);
+static void uv__udp_recvmsg(uv_udp_t* handle);
+static void uv__udp_sendmsg(uv_udp_t* handle);
+static int uv__udp_maybe_deferred_bind(uv_udp_t* handle,
+                                       int domain,
+                                       unsigned int flags);
+
+#if HAVE_MMSG
+
+#define UV__MMSG_MAXWIDTH 20
+
+static int uv__udp_recvmmsg(uv_udp_t* handle, uv_buf_t* buf);
+static void uv__udp_sendmmsg(uv_udp_t* handle);
+
+static int uv__recvmmsg_avail;
+static int uv__sendmmsg_avail;
+static uv_once_t once = UV_ONCE_INIT;
+
+static void uv__udp_mmsg_init(void) {
+  int ret;
+  int s;
+  s = uv__socket(AF_INET, SOCK_DGRAM, 0);
+  if (s < 0)
+    return;
+  ret = uv__sendmmsg(s, NULL, 0);
+  if (ret == 0 || errno != ENOSYS) {
+    uv__sendmmsg_avail = 1;
+    uv__recvmmsg_avail = 1;
+  } else {
+    ret = uv__recvmmsg(s, NULL, 0);
+    if (ret == 0 || errno != ENOSYS)
+      uv__recvmmsg_avail = 1;
+  }
+  uv__close(s);
+}
+
+#endif
+
+void uv__udp_close(uv_udp_t* handle) {
+  uv__io_close(handle->loop, &handle->io_watcher);
+  uv__handle_stop(handle);
+
+  if (handle->io_watcher.fd != -1) {
+    uv__close(handle->io_watcher.fd);
+    handle->io_watcher.fd = -1;
+  }
+}
+
+
+void uv__udp_finish_close(uv_udp_t* handle) {
+  uv_udp_send_t* req;
+  QUEUE* q;
+
+  assert(!uv__io_active(&handle->io_watcher, POLLIN | POLLOUT));
+  assert(handle->io_watcher.fd == -1);
+
+  while (!QUEUE_EMPTY(&handle->write_queue)) {
+    q = QUEUE_HEAD(&handle->write_queue);
+    QUEUE_REMOVE(q);
+
+    req = QUEUE_DATA(q, uv_udp_send_t, queue);
+    req->status = UV_ECANCELED;
+    QUEUE_INSERT_TAIL(&handle->write_completed_queue, &req->queue);
+  }
+
+  uv__udp_run_completed(handle);
+
+  assert(handle->send_queue_size == 0);
+  assert(handle->send_queue_count == 0);
+
+  /* Now tear down the handle. */
+  handle->recv_cb = NULL;
+  handle->alloc_cb = NULL;
+  /* but _do not_ touch close_cb */
+}
+
+
+static void uv__udp_run_completed(uv_udp_t* handle) {
+  uv_udp_send_t* req;
+  QUEUE* q;
+
+  assert(!(handle->flags & UV_HANDLE_UDP_PROCESSING));
+  handle->flags |= UV_HANDLE_UDP_PROCESSING;
+
+  while (!QUEUE_EMPTY(&handle->write_completed_queue)) {
+    q = QUEUE_HEAD(&handle->write_completed_queue);
+    QUEUE_REMOVE(q);
+
+    req = QUEUE_DATA(q, uv_udp_send_t, queue);
+    uv__req_unregister(handle->loop, req);
+
+    handle->send_queue_size -= uv__count_bufs(req->bufs, req->nbufs);
+    handle->send_queue_count--;
+
+    if (req->bufs != req->bufsml)
+      uv__free(req->bufs);
+    req->bufs = NULL;
+
+    if (req->send_cb == NULL)
+      continue;
+
+    /* req->status >= 0 == bytes written
+     * req->status <  0 == errno
+     */
+    if (req->status >= 0)
+      req->send_cb(req, 0);
+    else
+      req->send_cb(req, req->status);
+  }
+
+  if (QUEUE_EMPTY(&handle->write_queue)) {
+    /* Pending queue and completion queue empty, stop watcher. */
+    uv__io_stop(handle->loop, &handle->io_watcher, POLLOUT);
+    if (!uv__io_active(&handle->io_watcher, POLLIN))
+      uv__handle_stop(handle);
+  }
+
+  handle->flags &= ~UV_HANDLE_UDP_PROCESSING;
+}
+
+
+static void uv__udp_io(uv_loop_t* loop, uv__io_t* w, unsigned int revents) {
+  uv_udp_t* handle;
+
+  handle = container_of(w, uv_udp_t, io_watcher);
+  assert(handle->type == UV_UDP);
+
+  if (revents & POLLIN)
+    uv__udp_recvmsg(handle);
+
+  if (revents & POLLOUT) {
+    uv__udp_sendmsg(handle);
+    uv__udp_run_completed(handle);
+  }
+}
+
+#if HAVE_MMSG
+static int uv__udp_recvmmsg(uv_udp_t* handle, uv_buf_t* buf) {
+  struct sockaddr_in6 peers[UV__MMSG_MAXWIDTH];
+  struct iovec iov[UV__MMSG_MAXWIDTH];
+  struct uv__mmsghdr msgs[UV__MMSG_MAXWIDTH];
+  ssize_t nread;
+  uv_buf_t chunk_buf;
+  size_t chunks;
+  int flags;
+  size_t k;
+
+  /* prepare structures for recvmmsg */
+  chunks = buf->len / UV__UDP_DGRAM_MAXSIZE;
+  if (chunks > ARRAY_SIZE(iov))
+    chunks = ARRAY_SIZE(iov);
+  for (k = 0; k < chunks; ++k) {
+    iov[k].iov_base = buf->base + k * UV__UDP_DGRAM_MAXSIZE;
+    iov[k].iov_len = UV__UDP_DGRAM_MAXSIZE;
+    memset(&msgs[k].msg_hdr, 0, sizeof(msgs[k].msg_hdr));
+    msgs[k].msg_hdr.msg_iov = iov + k;
+    msgs[k].msg_hdr.msg_iovlen = 1;
+    msgs[k].msg_hdr.msg_name = peers + k;
+    msgs[k].msg_hdr.msg_namelen = sizeof(peers[0]);
+    msgs[k].msg_hdr.msg_control = NULL;
+    msgs[k].msg_hdr.msg_controllen = 0;
+    msgs[k].msg_hdr.msg_flags = 0;
+  }
+
+  do
+    nread = uv__recvmmsg(handle->io_watcher.fd, msgs, chunks);
+  while (nread == -1 && errno == EINTR);
+
+  if (nread < 1) {
+    if (nread == 0 || errno == EAGAIN || errno == EWOULDBLOCK)
+      handle->recv_cb(handle, 0, buf, NULL, 0);
+    else
+      handle->recv_cb(handle, UV__ERR(errno), buf, NULL, 0);
+  } else {
+    /* pass each chunk to the application */
+    for (k = 0; k < (size_t) nread && handle->recv_cb != NULL; k++) {
+      flags = UV_UDP_MMSG_CHUNK;
+      if (msgs[k].msg_hdr.msg_flags & MSG_TRUNC)
+        flags |= UV_UDP_PARTIAL;
+
+      chunk_buf = uv_buf_init((char*) iov[k].iov_base, iov[k].iov_len);
+      handle->recv_cb(handle,
+                      msgs[k].msg_len,
+                      &chunk_buf,
+                      (const sockaddr*) msgs[k].msg_hdr.msg_name,
+                      flags);
+    }
+
+    /* one last callback so the original buffer is freed */
+    if (handle->recv_cb != NULL)
+      handle->recv_cb(handle, 0, buf, NULL, UV_UDP_MMSG_FREE);
+  }
+  return nread;
+}
+#endif
+
+static void uv__udp_recvmsg(uv_udp_t* handle) {
+  struct sockaddr_storage peer;
+  struct msghdr h;
+  ssize_t nread;
+  uv_buf_t buf;
+  int flags;
+  int count;
+
+  assert(handle->recv_cb != NULL);
+  assert(handle->alloc_cb != NULL);
+
+  /* Prevent loop starvation when the data comes in as fast as (or faster than)
+   * we can read it. XXX Need to rearm fd if we switch to edge-triggered I/O.
+   */
+  count = 32;
+
+  do {
+    buf = uv_buf_init(NULL, 0);
+    handle->alloc_cb((uv_handle_t*) handle, UV__UDP_DGRAM_MAXSIZE, &buf);
+    if (buf.base == NULL || buf.len == 0) {
+      handle->recv_cb(handle, UV_ENOBUFS, &buf, NULL, 0);
+      return;
+    }
+    assert(buf.base != NULL);
+
+#if HAVE_MMSG
+    if (uv_udp_using_recvmmsg(handle)) {
+      nread = uv__udp_recvmmsg(handle, &buf);
+      if (nread > 0)
+        count -= nread;
+      continue;
+    }
+#endif
+
+    memset(&h, 0, sizeof(h));
+    memset(&peer, 0, sizeof(peer));
+    h.msg_name = &peer;
+    h.msg_namelen = sizeof(peer);
+    h.msg_iov = (iovec*) &buf;
+    h.msg_iovlen = 1;
+
+    do {
+      nread = recvmsg(handle->io_watcher.fd, &h, 0);
+    }
+    while (nread == -1 && errno == EINTR);
+
+    if (nread == -1) {
+      if (errno == EAGAIN || errno == EWOULDBLOCK)
+        handle->recv_cb(handle, 0, &buf, NULL, 0);
+      else
+        handle->recv_cb(handle, UV__ERR(errno), &buf, NULL, 0);
+    }
+    else {
+      flags = 0;
+      if (h.msg_flags & MSG_TRUNC)
+        flags |= UV_UDP_PARTIAL;
+
+      handle->recv_cb(handle, nread, &buf, (const struct sockaddr*) &peer, flags);
+    }
+    count--;
+  }
+  /* recv_cb callback may decide to pause or close the handle */
+  while (nread != -1
+      && count > 0
+      && handle->io_watcher.fd != -1
+      && handle->recv_cb != NULL);
+}
+
+#if HAVE_MMSG
+static void uv__udp_sendmmsg(uv_udp_t* handle) {
+  uv_udp_send_t* req;
+  struct uv__mmsghdr h[UV__MMSG_MAXWIDTH];
+  struct uv__mmsghdr *p;
+  QUEUE* q;
+  ssize_t npkts;
+  size_t pkts;
+  size_t i;
+
+  if (QUEUE_EMPTY(&handle->write_queue))
+    return;
+
+write_queue_drain:
+  for (pkts = 0, q = QUEUE_HEAD(&handle->write_queue);
+       pkts < UV__MMSG_MAXWIDTH && q != &handle->write_queue;
+       ++pkts, q = QUEUE_HEAD(q)) {
+    assert(q != NULL);
+    req = QUEUE_DATA(q, uv_udp_send_t, queue);
+    assert(req != NULL);
+
+    p = &h[pkts];
+    memset(p, 0, sizeof(*p));
+    if (req->addr.ss_family == AF_UNSPEC) {
+      p->msg_hdr.msg_name = NULL;
+      p->msg_hdr.msg_namelen = 0;
+    } else {
+      p->msg_hdr.msg_name = &req->addr;
+      if (req->addr.ss_family == AF_INET6)
+        p->msg_hdr.msg_namelen = sizeof(struct sockaddr_in6);
+      else if (req->addr.ss_family == AF_INET)
+        p->msg_hdr.msg_namelen = sizeof(struct sockaddr_in);
+      else if (req->addr.ss_family == AF_UNIX)
+        p->msg_hdr.msg_namelen = sizeof(struct sockaddr_un);
+      else {
+        assert(0 && "unsupported address family");
+        abort();
+      }
+    }
+    h[pkts].msg_hdr.msg_iov = (struct iovec*) req->bufs;
+    h[pkts].msg_hdr.msg_iovlen = req->nbufs;
+  }
+
+  do
+    npkts = uv__sendmmsg(handle->io_watcher.fd, h, pkts);
+  while (npkts == -1 && errno == EINTR);
+
+  if (npkts < 1) {
+    if (errno == EAGAIN || errno == EWOULDBLOCK || errno == ENOBUFS)
+      return;
+    for (i = 0, q = QUEUE_HEAD(&handle->write_queue);
+         i < pkts && q != &handle->write_queue;
+         ++i, q = QUEUE_HEAD(&handle->write_queue)) {
+      assert(q != NULL);
+      req = QUEUE_DATA(q, uv_udp_send_t, queue);
+      assert(req != NULL);
+
+      req->status = UV__ERR(errno);
+      QUEUE_REMOVE(&req->queue);
+      QUEUE_INSERT_TAIL(&handle->write_completed_queue, &req->queue);
+    }
+    uv__io_feed(handle->loop, &handle->io_watcher);
+    return;
+  }
+
+  /* Safety: npkts known to be >0 below. Hence cast from ssize_t
+   * to size_t safe.
+   */
+  for (i = 0, q = QUEUE_HEAD(&handle->write_queue);
+       i < (size_t)npkts && q != &handle->write_queue;
+       ++i, q = QUEUE_HEAD(&handle->write_queue)) {
+    assert(q != NULL);
+    req = QUEUE_DATA(q, uv_udp_send_t, queue);
+    assert(req != NULL);
+
+    req->status = req->bufs[0].len;
+
+    /* Sending a datagram is an atomic operation: either all data
+     * is written or nothing is (and EMSGSIZE is raised). That is
+     * why we don't handle partial writes. Just pop the request
+     * off the write queue and onto the completed queue, done.
+     */
+    QUEUE_REMOVE(&req->queue);
+    QUEUE_INSERT_TAIL(&handle->write_completed_queue, &req->queue);
+  }
+
+  /* couldn't batch everything, continue sending (jump to avoid stack growth) */
+  if (!QUEUE_EMPTY(&handle->write_queue))
+    goto write_queue_drain;
+  uv__io_feed(handle->loop, &handle->io_watcher);
+  return;
+}
+#endif
+
+static void uv__udp_sendmsg(uv_udp_t* handle) {
+  uv_udp_send_t* req;
+  struct msghdr h;
+  QUEUE* q;
+  ssize_t size;
+
+#if HAVE_MMSG
+  uv_once(&once, uv__udp_mmsg_init);
+  if (uv__sendmmsg_avail) {
+    uv__udp_sendmmsg(handle);
+    return;
+  }
+#endif
+
+  while (!QUEUE_EMPTY(&handle->write_queue)) {
+    q = QUEUE_HEAD(&handle->write_queue);
+    assert(q != NULL);
+
+    req = QUEUE_DATA(q, uv_udp_send_t, queue);
+    assert(req != NULL);
+
+    memset(&h, 0, sizeof h);
+    if (req->addr.ss_family == AF_UNSPEC) {
+      h.msg_name = NULL;
+      h.msg_namelen = 0;
+    } else {
+      h.msg_name = &req->addr;
+      if (req->addr.ss_family == AF_INET6)
+        h.msg_namelen = sizeof(struct sockaddr_in6);
+      else if (req->addr.ss_family == AF_INET)
+        h.msg_namelen = sizeof(struct sockaddr_in);
+      else if (req->addr.ss_family == AF_UNIX)
+        h.msg_namelen = sizeof(struct sockaddr_un);
+      else {
+        assert(0 && "unsupported address family");
+        abort();
+      }
+    }
+    h.msg_iov = (struct iovec*) req->bufs;
+    h.msg_iovlen = req->nbufs;
+
+    do {
+      size = sendmsg(handle->io_watcher.fd, &h, 0);
+    } while (size == -1 && errno == EINTR);
+
+    if (size == -1) {
+      if (errno == EAGAIN || errno == EWOULDBLOCK || errno == ENOBUFS)
+        break;
+    }
+
+    req->status = (size == -1 ? UV__ERR(errno) : size);
+
+    /* Sending a datagram is an atomic operation: either all data
+     * is written or nothing is (and EMSGSIZE is raised). That is
+     * why we don't handle partial writes. Just pop the request
+     * off the write queue and onto the completed queue, done.
+     */
+    QUEUE_REMOVE(&req->queue);
+    QUEUE_INSERT_TAIL(&handle->write_completed_queue, &req->queue);
+    uv__io_feed(handle->loop, &handle->io_watcher);
+  }
+}
+
+/* On the BSDs, SO_REUSEPORT implies SO_REUSEADDR but with some additional
+ * refinements for programs that use multicast.
+ *
+ * Linux as of 3.9 has a SO_REUSEPORT socket option but with semantics that
+ * are different from the BSDs: it _shares_ the port rather than steal it
+ * from the current listener.  While useful, it's not something we can emulate
+ * on other platforms so we don't enable it.
+ *
+ * zOS does not support getsockname with SO_REUSEPORT option when using
+ * AF_UNIX.
+ */
+static int uv__set_reuse(int fd) {
+  int yes;
+  yes = 1;
+
+#if defined(SO_REUSEPORT) && defined(__MVS__)
+  struct sockaddr_in sockfd;
+  unsigned int sockfd_len = sizeof(sockfd);
+  if (getsockname(fd, (struct sockaddr*) &sockfd, &sockfd_len) == -1)
+      return UV__ERR(errno);
+  if (sockfd.sin_family == AF_UNIX) {
+    if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)))
+      return UV__ERR(errno);
+  } else {
+    if (setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &yes, sizeof(yes)))
+       return UV__ERR(errno);
+  }
+#elif defined(SO_REUSEPORT) && !defined(__linux__) && !defined(__GNU__)
+  if (setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &yes, sizeof(yes)))
+    return UV__ERR(errno);
+#else
+  if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)))
+    return UV__ERR(errno);
+#endif
+
+  return 0;
+}
+
+/*
+ * The Linux kernel suppresses some ICMP error messages by default for UDP
+ * sockets. Setting IP_RECVERR/IPV6_RECVERR on the socket enables full ICMP
+ * error reporting, hopefully resulting in faster failover to working name
+ * servers.
+ */
+static int uv__set_recverr(int fd, sa_family_t ss_family) {
+#if defined(__linux__)
+  int yes;
+
+  yes = 1;
+  if (ss_family == AF_INET) {
+    if (setsockopt(fd, IPPROTO_IP, IP_RECVERR, &yes, sizeof(yes)))
+      return UV__ERR(errno);
+  } else if (ss_family == AF_INET6) {
+    if (setsockopt(fd, IPPROTO_IPV6, IPV6_RECVERR, &yes, sizeof(yes)))
+       return UV__ERR(errno);
+  }
+#endif
+  return 0;
+}
+
+
+int uv__udp_bind(uv_udp_t* handle,
+                 const struct sockaddr* addr,
+                 unsigned int addrlen,
+                 unsigned int flags) {
+  int err;
+  int yes;
+  int fd;
+
+  /* Check for bad flags. */
+  if (flags & ~(UV_UDP_IPV6ONLY | UV_UDP_REUSEADDR | UV_UDP_LINUX_RECVERR))
+    return UV_EINVAL;
+
+  /* Cannot set IPv6-only mode on non-IPv6 socket. */
+  if ((flags & UV_UDP_IPV6ONLY) && addr->sa_family != AF_INET6)
+    return UV_EINVAL;
+
+  fd = handle->io_watcher.fd;
+  if (fd == -1) {
+    err = uv__socket(addr->sa_family, SOCK_DGRAM, 0);
+    if (err < 0)
+      return err;
+    fd = err;
+    handle->io_watcher.fd = fd;
+  }
+
+  if (flags & UV_UDP_LINUX_RECVERR) {
+    err = uv__set_recverr(fd, addr->sa_family);
+    if (err)
+      return err;
+  }
+
+  if (flags & UV_UDP_REUSEADDR) {
+    err = uv__set_reuse(fd);
+    if (err)
+      return err;
+  }
+
+  if (flags & UV_UDP_IPV6ONLY) {
+#ifdef IPV6_V6ONLY
+    yes = 1;
+    if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &yes, sizeof yes) == -1) {
+      err = UV__ERR(errno);
+      return err;
+    }
+#else
+    err = UV_ENOTSUP;
+    return err;
+#endif
+  }
+
+  if (bind(fd, addr, addrlen)) {
+    err = UV__ERR(errno);
+    if (errno == EAFNOSUPPORT)
+      /* OSX, other BSDs and SunoS fail with EAFNOSUPPORT when binding a
+       * socket created with AF_INET to an AF_INET6 address or vice versa. */
+      err = UV_EINVAL;
+    return err;
+  }
+
+  if (addr->sa_family == AF_INET6)
+    handle->flags |= UV_HANDLE_IPV6;
+
+  handle->flags |= UV_HANDLE_BOUND;
+  return 0;
+}
+
+
+static int uv__udp_maybe_deferred_bind(uv_udp_t* handle,
+                                       int domain,
+                                       unsigned int flags) {
+  union uv__sockaddr taddr;
+  socklen_t addrlen;
+
+  if (handle->io_watcher.fd != -1)
+    return 0;
+
+  switch (domain) {
+  case AF_INET:
+  {
+    struct sockaddr_in* addr = &taddr.in;
+    memset(addr, 0, sizeof *addr);
+    addr->sin_family = AF_INET;
+    addr->sin_addr.s_addr = INADDR_ANY;
+    addrlen = sizeof *addr;
+    break;
+  }
+  case AF_INET6:
+  {
+    struct sockaddr_in6* addr = &taddr.in6;
+    memset(addr, 0, sizeof *addr);
+    addr->sin6_family = AF_INET6;
+    addr->sin6_addr = in6addr_any;
+    addrlen = sizeof *addr;
+    break;
+  }
+  default:
+    assert(0 && "unsupported address family");
+    abort();
+  }
+
+  return uv__udp_bind(handle, &taddr.addr, addrlen, flags);
+}
+
+
+int uv__udp_connect(uv_udp_t* handle,
+                    const struct sockaddr* addr,
+                    unsigned int addrlen) {
+  int err;
+
+  err = uv__udp_maybe_deferred_bind(handle, addr->sa_family, 0);
+  if (err)
+    return err;
+
+  do {
+    errno = 0;
+    err = connect(handle->io_watcher.fd, addr, addrlen);
+  } while (err == -1 && errno == EINTR);
+
+  if (err)
+    return UV__ERR(errno);
+
+  handle->flags |= UV_HANDLE_UDP_CONNECTED;
+
+  return 0;
+}
+
+/* From https://pubs.opengroup.org/onlinepubs/9699919799/functions/connect.html
+ * Any of uv supported UNIXs kernel should be standardized, but the kernel
+ * implementation logic not same, let's use pseudocode to explain the udp
+ * disconnect behaviors:
+ *
+ * Predefined stubs for pseudocode:
+ *   1. sodisconnect: The function to perform the real udp disconnect
+ *   2. pru_connect: The function to perform the real udp connect
+ *   3. so: The kernel object match with socket fd
+ *   4. addr: The sockaddr parameter from user space
+ *
+ * BSDs:
+ *   if(sodisconnect(so) == 0) { // udp disconnect succeed
+ *     if (addr->sa_len != so->addr->sa_len) return EINVAL;
+ *     if (addr->sa_family != so->addr->sa_family) return EAFNOSUPPORT;
+ *     pru_connect(so);
+ *   }
+ *   else return EISCONN;
+ *
+ * z/OS (same with Windows):
+ *   if(addr->sa_len < so->addr->sa_len) return EINVAL;
+ *   if (addr->sa_family == AF_UNSPEC) sodisconnect(so);
+ *
+ * AIX:
+ *   if(addr->sa_len != sizeof(struct sockaddr)) return EINVAL; // ignore ip proto version
+ *   if (addr->sa_family == AF_UNSPEC) sodisconnect(so);
+ *
+ * Linux,Others:
+ *   if(addr->sa_len < sizeof(struct sockaddr)) return EINVAL;
+ *   if (addr->sa_family == AF_UNSPEC) sodisconnect(so);
+ */
+int uv__udp_disconnect(uv_udp_t* handle) {
+    int r;
+#if defined(__MVS__)
+    struct sockaddr_storage addr;
+#else
+    struct sockaddr addr;
+#endif
+
+    memset(&addr, 0, sizeof(addr));
+
+#if defined(__MVS__)
+    addr.ss_family = AF_UNSPEC;
+#else
+    addr.sa_family = AF_UNSPEC;
+#endif
+
+    do {
+      errno = 0;
+#ifdef __PASE__
+      /* On IBMi a connectionless transport socket can be disconnected by
+       * either setting the addr parameter to NULL or setting the
+       * addr_length parameter to zero, and issuing another connect().
+       * https://www.ibm.com/docs/en/i/7.4?topic=ssw_ibm_i_74/apis/connec.htm
+       */
+      r = connect(handle->io_watcher.fd, (struct sockaddr*) NULL, 0);
+#else
+      r = connect(handle->io_watcher.fd, (struct sockaddr*) &addr, sizeof(addr));
+#endif
+    } while (r == -1 && errno == EINTR);
+
+    if (r == -1) {
+#if defined(BSD)  /* The macro BSD is from sys/param.h */
+      if (errno != EAFNOSUPPORT && errno != EINVAL)
+        return UV__ERR(errno);
+#else
+      return UV__ERR(errno);
+#endif
+    }
+
+    handle->flags &= ~UV_HANDLE_UDP_CONNECTED;
+    return 0;
+}
+
+int uv__udp_send(uv_udp_send_t* req,
+                 uv_udp_t* handle,
+                 const uv_buf_t bufs[],
+                 unsigned int nbufs,
+                 const struct sockaddr* addr,
+                 unsigned int addrlen,
+                 uv_udp_send_cb send_cb) {
+  int err;
+  int empty_queue;
+
+  assert(nbufs > 0);
+
+  if (addr) {
+    err = uv__udp_maybe_deferred_bind(handle, addr->sa_family, 0);
+    if (err)
+      return err;
+  }
+
+  /* It's legal for send_queue_count > 0 even when the write_queue is empty;
+   * it means there are error-state requests in the write_completed_queue that
+   * will touch up send_queue_size/count later.
+   */
+  empty_queue = (handle->send_queue_count == 0);
+
+  uv__req_init(handle->loop, req, UV_UDP_SEND);
+  assert(addrlen <= sizeof(req->addr));
+  if (addr == NULL)
+    req->addr.ss_family = AF_UNSPEC;
+  else
+    memcpy(&req->addr, addr, addrlen);
+  req->send_cb = send_cb;
+  req->handle = handle;
+  req->nbufs = nbufs;
+
+  req->bufs = req->bufsml;
+  if (nbufs > ARRAY_SIZE(req->bufsml))
+    req->bufs = (uv_buf_t*)uv__malloc(nbufs * sizeof(bufs[0]));
+
+  if (req->bufs == NULL) {
+    uv__req_unregister(handle->loop, req);
+    return UV_ENOMEM;
+  }
+
+  memcpy(req->bufs, bufs, nbufs * sizeof(bufs[0]));
+  handle->send_queue_size += uv__count_bufs(req->bufs, req->nbufs);
+  handle->send_queue_count++;
+  QUEUE_INSERT_TAIL(&handle->write_queue, &req->queue);
+  uv__handle_start(handle);
+
+  if (empty_queue && !(handle->flags & UV_HANDLE_UDP_PROCESSING)) {
+    uv__udp_sendmsg(handle);
+
+    /* `uv__udp_sendmsg` may not be able to do non-blocking write straight
+     * away. In such cases the `io_watcher` has to be queued for asynchronous
+     * write.
+     */
+    if (!QUEUE_EMPTY(&handle->write_queue))
+      uv__io_start(handle->loop, &handle->io_watcher, POLLOUT);
+  } else {
+    uv__io_start(handle->loop, &handle->io_watcher, POLLOUT);
+  }
+
+  return 0;
+}
+
+
+int uv__udp_try_send(uv_udp_t* handle,
+                     const uv_buf_t bufs[],
+                     unsigned int nbufs,
+                     const struct sockaddr* addr,
+                     unsigned int addrlen) {
+  int err;
+  struct msghdr h;
+  ssize_t size;
+
+  assert(nbufs > 0);
+
+  /* already sending a message */
+  if (handle->send_queue_count != 0)
+    return UV_EAGAIN;
+
+  if (addr) {
+    err = uv__udp_maybe_deferred_bind(handle, addr->sa_family, 0);
+    if (err)
+      return err;
+  } else {
+    assert(handle->flags & UV_HANDLE_UDP_CONNECTED);
+  }
+
+  memset(&h, 0, sizeof h);
+  h.msg_name = (struct sockaddr*) addr;
+  h.msg_namelen = addrlen;
+  h.msg_iov = (struct iovec*) bufs;
+  h.msg_iovlen = nbufs;
+
+  do {
+    size = sendmsg(handle->io_watcher.fd, &h, 0);
+  } while (size == -1 && errno == EINTR);
+
+  if (size == -1) {
+    if (errno == EAGAIN || errno == EWOULDBLOCK || errno == ENOBUFS)
+      return UV_EAGAIN;
+    else
+      return UV__ERR(errno);
+  }
+
+  return size;
+}
+
+
+static int uv__udp_set_membership4(uv_udp_t* handle,
+                                   const struct sockaddr_in* multicast_addr,
+                                   const char* interface_addr,
+                                   uv_membership membership) {
+  struct ip_mreq mreq;
+  int optname;
+  int err;
+
+  memset(&mreq, 0, sizeof mreq);
+
+  if (interface_addr) {
+    err = uv_inet_pton(AF_INET, interface_addr, &mreq.imr_interface.s_addr);
+    if (err)
+      return err;
+  } else {
+    mreq.imr_interface.s_addr = htonl(INADDR_ANY);
+  }
+
+  mreq.imr_multiaddr.s_addr = multicast_addr->sin_addr.s_addr;
+
+  switch (membership) {
+  case UV_JOIN_GROUP:
+    optname = IP_ADD_MEMBERSHIP;
+    break;
+  case UV_LEAVE_GROUP:
+    optname = IP_DROP_MEMBERSHIP;
+    break;
+  default:
+    return UV_EINVAL;
+  }
+
+  if (setsockopt(handle->io_watcher.fd,
+                 IPPROTO_IP,
+                 optname,
+                 &mreq,
+                 sizeof(mreq))) {
+#if defined(__MVS__)
+  if (errno == ENXIO)
+    return UV_ENODEV;
+#endif
+    return UV__ERR(errno);
+  }
+
+  return 0;
+}
+
+
+static int uv__udp_set_membership6(uv_udp_t* handle,
+                                   const struct sockaddr_in6* multicast_addr,
+                                   const char* interface_addr,
+                                   uv_membership membership) {
+  int optname;
+  struct ipv6_mreq mreq;
+  struct sockaddr_in6 addr6;
+
+  memset(&mreq, 0, sizeof mreq);
+
+  if (interface_addr) {
+    if (uv_ip6_addr(interface_addr, 0, &addr6))
+      return UV_EINVAL;
+    mreq.ipv6mr_interface = addr6.sin6_scope_id;
+  } else {
+    mreq.ipv6mr_interface = 0;
+  }
+
+  mreq.ipv6mr_multiaddr = multicast_addr->sin6_addr;
+
+  switch (membership) {
+  case UV_JOIN_GROUP:
+    optname = IPV6_ADD_MEMBERSHIP;
+    break;
+  case UV_LEAVE_GROUP:
+    optname = IPV6_DROP_MEMBERSHIP;
+    break;
+  default:
+    return UV_EINVAL;
+  }
+
+  if (setsockopt(handle->io_watcher.fd,
+                 IPPROTO_IPV6,
+                 optname,
+                 &mreq,
+                 sizeof(mreq))) {
+#if defined(__MVS__)
+  if (errno == ENXIO)
+    return UV_ENODEV;
+#endif
+    return UV__ERR(errno);
+  }
+
+  return 0;
+}
+
+
+#if !defined(__OpenBSD__) &&                                        \
+    !defined(__NetBSD__) &&                                         \
+    !defined(__ANDROID__) &&                                        \
+    !defined(__DragonFly__) &&                                      \
+    !defined(__QNX__) &&                                            \
+    !defined(__GNU__)
+static int uv__udp_set_source_membership4(uv_udp_t* handle,
+                                          const struct sockaddr_in* multicast_addr,
+                                          const char* interface_addr,
+                                          const struct sockaddr_in* source_addr,
+                                          uv_membership membership) {
+  struct ip_mreq_source mreq;
+  int optname;
+  int err;
+
+  err = uv__udp_maybe_deferred_bind(handle, AF_INET, UV_UDP_REUSEADDR);
+  if (err)
+    return err;
+
+  memset(&mreq, 0, sizeof(mreq));
+
+  if (interface_addr != NULL) {
+    err = uv_inet_pton(AF_INET, interface_addr, &mreq.imr_interface.s_addr);
+    if (err)
+      return err;
+  } else {
+    mreq.imr_interface.s_addr = htonl(INADDR_ANY);
+  }
+
+  mreq.imr_multiaddr.s_addr = multicast_addr->sin_addr.s_addr;
+  mreq.imr_sourceaddr.s_addr = source_addr->sin_addr.s_addr;
+
+  if (membership == UV_JOIN_GROUP)
+    optname = IP_ADD_SOURCE_MEMBERSHIP;
+  else if (membership == UV_LEAVE_GROUP)
+    optname = IP_DROP_SOURCE_MEMBERSHIP;
+  else
+    return UV_EINVAL;
+
+  if (setsockopt(handle->io_watcher.fd,
+                 IPPROTO_IP,
+                 optname,
+                 &mreq,
+                 sizeof(mreq))) {
+    return UV__ERR(errno);
+  }
+
+  return 0;
+}
+
+
+static int uv__udp_set_source_membership6(uv_udp_t* handle,
+                                          const struct sockaddr_in6* multicast_addr,
+                                          const char* interface_addr,
+                                          const struct sockaddr_in6* source_addr,
+                                          uv_membership membership) {
+  struct group_source_req mreq;
+  struct sockaddr_in6 addr6;
+  int optname;
+  int err;
+
+  err = uv__udp_maybe_deferred_bind(handle, AF_INET6, UV_UDP_REUSEADDR);
+  if (err)
+    return err;
+
+  memset(&mreq, 0, sizeof(mreq));
+
+  if (interface_addr != NULL) {
+    err = uv_ip6_addr(interface_addr, 0, &addr6);
+    if (err)
+      return err;
+    mreq.gsr_interface = addr6.sin6_scope_id;
+  } else {
+    mreq.gsr_interface = 0;
+  }
+
+  STATIC_ASSERT(sizeof(mreq.gsr_group) >= sizeof(*multicast_addr));
+  STATIC_ASSERT(sizeof(mreq.gsr_source) >= sizeof(*source_addr));
+  memcpy(&mreq.gsr_group, multicast_addr, sizeof(*multicast_addr));
+  memcpy(&mreq.gsr_source, source_addr, sizeof(*source_addr));
+
+  if (membership == UV_JOIN_GROUP)
+    optname = MCAST_JOIN_SOURCE_GROUP;
+  else if (membership == UV_LEAVE_GROUP)
+    optname = MCAST_LEAVE_SOURCE_GROUP;
+  else
+    return UV_EINVAL;
+
+  if (setsockopt(handle->io_watcher.fd,
+                 IPPROTO_IPV6,
+                 optname,
+                 &mreq,
+                 sizeof(mreq))) {
+    return UV__ERR(errno);
+  }
+
+  return 0;
+}
+#endif
+
+
+int uv__udp_init_ex(uv_loop_t* loop,
+                    uv_udp_t* handle,
+                    unsigned flags,
+                    int domain) {
+  int fd;
+
+  fd = -1;
+  if (domain != AF_UNSPEC) {
+    fd = uv__socket(domain, SOCK_DGRAM, 0);
+    if (fd < 0)
+      return fd;
+  }
+
+  uv__handle_init(loop, (uv_handle_t*)handle, UV_UDP);
+  handle->alloc_cb = NULL;
+  handle->recv_cb = NULL;
+  handle->send_queue_size = 0;
+  handle->send_queue_count = 0;
+  uv__io_init(&handle->io_watcher, uv__udp_io, fd);
+  QUEUE_INIT(&handle->write_queue);
+  QUEUE_INIT(&handle->write_completed_queue);
+
+  return 0;
+}
+
+
+int uv_udp_using_recvmmsg(const uv_udp_t* handle) {
+#if HAVE_MMSG
+  if (handle->flags & UV_HANDLE_UDP_RECVMMSG) {
+    uv_once(&once, uv__udp_mmsg_init);
+    return uv__recvmmsg_avail;
+  }
+#endif
+  return 0;
+}
+
+
+int uv_udp_open(uv_udp_t* handle, uv_os_sock_t sock) {
+  int err;
+
+  /* Check for already active socket. */
+  if (handle->io_watcher.fd != -1)
+    return UV_EBUSY;
+
+  if (uv__fd_exists(handle->loop, sock))
+    return UV_EEXIST;
+
+  err = uv__nonblock(sock, 1);
+  if (err)
+    return err;
+
+  err = uv__set_reuse(sock);
+  if (err)
+    return err;
+
+  handle->io_watcher.fd = sock;
+  if (uv__udp_is_connected(handle))
+    handle->flags |= UV_HANDLE_UDP_CONNECTED;
+
+  return 0;
+}
+
+
+int uv_udp_set_membership(uv_udp_t* handle,
+                          const char* multicast_addr,
+                          const char* interface_addr,
+                          uv_membership membership) {
+  int err;
+  struct sockaddr_in addr4;
+  struct sockaddr_in6 addr6;
+
+  if (uv_ip4_addr(multicast_addr, 0, &addr4) == 0) {
+    err = uv__udp_maybe_deferred_bind(handle, AF_INET, UV_UDP_REUSEADDR);
+    if (err)
+      return err;
+    return uv__udp_set_membership4(handle, &addr4, interface_addr, membership);
+  } else if (uv_ip6_addr(multicast_addr, 0, &addr6) == 0) {
+    err = uv__udp_maybe_deferred_bind(handle, AF_INET6, UV_UDP_REUSEADDR);
+    if (err)
+      return err;
+    return uv__udp_set_membership6(handle, &addr6, interface_addr, membership);
+  } else {
+    return UV_EINVAL;
+  }
+}
+
+
+int uv_udp_set_source_membership(uv_udp_t* handle,
+                                 const char* multicast_addr,
+                                 const char* interface_addr,
+                                 const char* source_addr,
+                                 uv_membership membership) {
+#if !defined(__OpenBSD__) &&                                        \
+    !defined(__NetBSD__) &&                                         \
+    !defined(__ANDROID__) &&                                        \
+    !defined(__DragonFly__) &&                                      \
+    !defined(__QNX__) &&                                            \
+    !defined(__GNU__)
+  int err;
+  union uv__sockaddr mcast_addr;
+  union uv__sockaddr src_addr;
+
+  err = uv_ip4_addr(multicast_addr, 0, &mcast_addr.in);
+  if (err) {
+    err = uv_ip6_addr(multicast_addr, 0, &mcast_addr.in6);
+    if (err)
+      return err;
+    err = uv_ip6_addr(source_addr, 0, &src_addr.in6);
+    if (err)
+      return err;
+    return uv__udp_set_source_membership6(handle,
+                                          &mcast_addr.in6,
+                                          interface_addr,
+                                          &src_addr.in6,
+                                          membership);
+  }
+
+  err = uv_ip4_addr(source_addr, 0, &src_addr.in);
+  if (err)
+    return err;
+  return uv__udp_set_source_membership4(handle,
+                                        &mcast_addr.in,
+                                        interface_addr,
+                                        &src_addr.in,
+                                        membership);
+#else
+  return UV_ENOSYS;
+#endif
+}
+
+
+static int uv__setsockopt(uv_udp_t* handle,
+                         int option4,
+                         int option6,
+                         const void* val,
+                         socklen_t size) {
+  int r;
+
+  if (handle->flags & UV_HANDLE_IPV6)
+    r = setsockopt(handle->io_watcher.fd,
+                   IPPROTO_IPV6,
+                   option6,
+                   val,
+                   size);
+  else
+    r = setsockopt(handle->io_watcher.fd,
+                   IPPROTO_IP,
+                   option4,
+                   val,
+                   size);
+  if (r)
+    return UV__ERR(errno);
+
+  return 0;
+}
+
+static int uv__setsockopt_maybe_char(uv_udp_t* handle,
+                                     int option4,
+                                     int option6,
+                                     int val) {
+#if defined(__sun) || defined(_AIX) || defined(__MVS__)
+  char arg = val;
+#elif defined(__OpenBSD__)
+  unsigned char arg = val;
+#else
+  int arg = val;
+#endif
+
+  if (val < 0 || val > 255)
+    return UV_EINVAL;
+
+  return uv__setsockopt(handle, option4, option6, &arg, sizeof(arg));
+}
+
+
+int uv_udp_set_broadcast(uv_udp_t* handle, int on) {
+  if (setsockopt(handle->io_watcher.fd,
+                 SOL_SOCKET,
+                 SO_BROADCAST,
+                 &on,
+                 sizeof(on))) {
+    return UV__ERR(errno);
+  }
+
+  return 0;
+}
+
+
+int uv_udp_set_ttl(uv_udp_t* handle, int ttl) {
+  if (ttl < 1 || ttl > 255)
+    return UV_EINVAL;
+
+#if defined(__MVS__)
+  if (!(handle->flags & UV_HANDLE_IPV6))
+    return UV_ENOTSUP;  /* zOS does not support setting ttl for IPv4 */
+#endif
+
+/*
+ * On Solaris and derivatives such as SmartOS, the length of socket options
+ * is sizeof(int) for IP_TTL and IPV6_UNICAST_HOPS,
+ * so hardcode the size of these options on this platform,
+ * and use the general uv__setsockopt_maybe_char call on other platforms.
+ */
+#if defined(__sun) || defined(_AIX) || defined(__OpenBSD__) || \
+    defined(__MVS__) || defined(__QNX__)
+
+  return uv__setsockopt(handle,
+                        IP_TTL,
+                        IPV6_UNICAST_HOPS,
+                        &ttl,
+                        sizeof(ttl));
+
+#else /* !(defined(__sun) || defined(_AIX) || defined (__OpenBSD__) ||
+           defined(__MVS__) || defined(__QNX__)) */
+
+  return uv__setsockopt_maybe_char(handle,
+                                   IP_TTL,
+                                   IPV6_UNICAST_HOPS,
+                                   ttl);
+
+#endif /* defined(__sun) || defined(_AIX) || defined (__OpenBSD__) ||
+          defined(__MVS__) || defined(__QNX__) */
+}
+
+
+int uv_udp_set_multicast_ttl(uv_udp_t* handle, int ttl) {
+/*
+ * On Solaris and derivatives such as SmartOS, the length of socket options
+ * is sizeof(int) for IPV6_MULTICAST_HOPS and sizeof(char) for
+ * IP_MULTICAST_TTL, so hardcode the size of the option in the IPv6 case,
+ * and use the general uv__setsockopt_maybe_char call otherwise.
+ */
+#if defined(__sun) || defined(_AIX) || defined(__OpenBSD__) || \
+    defined(__MVS__) || defined(__QNX__)
+  if (handle->flags & UV_HANDLE_IPV6)
+    return uv__setsockopt(handle,
+                          IP_MULTICAST_TTL,
+                          IPV6_MULTICAST_HOPS,
+                          &ttl,
+                          sizeof(ttl));
+#endif /* defined(__sun) || defined(_AIX) || defined(__OpenBSD__) || \
+    defined(__MVS__) || defined(__QNX__) */
+
+  return uv__setsockopt_maybe_char(handle,
+                                   IP_MULTICAST_TTL,
+                                   IPV6_MULTICAST_HOPS,
+                                   ttl);
+}
+
+
+int uv_udp_set_multicast_loop(uv_udp_t* handle, int on) {
+/*
+ * On Solaris and derivatives such as SmartOS, the length of socket options
+ * is sizeof(int) for IPV6_MULTICAST_LOOP and sizeof(char) for
+ * IP_MULTICAST_LOOP, so hardcode the size of the option in the IPv6 case,
+ * and use the general uv__setsockopt_maybe_char call otherwise.
+ */
+#if defined(__sun) || defined(_AIX) || defined(__OpenBSD__) || \
+    defined(__MVS__) || defined(__QNX__)
+  if (handle->flags & UV_HANDLE_IPV6)
+    return uv__setsockopt(handle,
+                          IP_MULTICAST_LOOP,
+                          IPV6_MULTICAST_LOOP,
+                          &on,
+                          sizeof(on));
+#endif /* defined(__sun) || defined(_AIX) ||defined(__OpenBSD__) ||
+    defined(__MVS__) || defined(__QNX__) */
+
+  return uv__setsockopt_maybe_char(handle,
+                                   IP_MULTICAST_LOOP,
+                                   IPV6_MULTICAST_LOOP,
+                                   on);
+}
+
+int uv_udp_set_multicast_interface(uv_udp_t* handle, const char* interface_addr) {
+  struct sockaddr_storage addr_st;
+  struct sockaddr_in* addr4;
+  struct sockaddr_in6* addr6;
+
+  addr4 = (struct sockaddr_in*) &addr_st;
+  addr6 = (struct sockaddr_in6*) &addr_st;
+
+  if (!interface_addr) {
+    memset(&addr_st, 0, sizeof addr_st);
+    if (handle->flags & UV_HANDLE_IPV6) {
+      addr_st.ss_family = AF_INET6;
+      addr6->sin6_scope_id = 0;
+    } else {
+      addr_st.ss_family = AF_INET;
+      addr4->sin_addr.s_addr = htonl(INADDR_ANY);
+    }
+  } else if (uv_ip4_addr(interface_addr, 0, addr4) == 0) {
+    /* nothing, address was parsed */
+  } else if (uv_ip6_addr(interface_addr, 0, addr6) == 0) {
+    /* nothing, address was parsed */
+  } else {
+    return UV_EINVAL;
+  }
+
+  if (addr_st.ss_family == AF_INET) {
+    if (setsockopt(handle->io_watcher.fd,
+                   IPPROTO_IP,
+                   IP_MULTICAST_IF,
+                   (void*) &addr4->sin_addr,
+                   sizeof(addr4->sin_addr)) == -1) {
+      return UV__ERR(errno);
+    }
+  } else if (addr_st.ss_family == AF_INET6) {
+    if (setsockopt(handle->io_watcher.fd,
+                   IPPROTO_IPV6,
+                   IPV6_MULTICAST_IF,
+                   &addr6->sin6_scope_id,
+                   sizeof(addr6->sin6_scope_id)) == -1) {
+      return UV__ERR(errno);
+    }
+  } else {
+    assert(0 && "unexpected address family");
+    abort();
+  }
+
+  return 0;
+}
+
+int uv_udp_getpeername(const uv_udp_t* handle,
+                       struct sockaddr* name,
+                       int* namelen) {
+
+  return uv__getsockpeername((const uv_handle_t*) handle,
+                             getpeername,
+                             name,
+                             namelen);
+}
+
+int uv_udp_getsockname(const uv_udp_t* handle,
+                       struct sockaddr* name,
+                       int* namelen) {
+
+  return uv__getsockpeername((const uv_handle_t*) handle,
+                             getsockname,
+                             name,
+                             namelen);
+}
+
+
+int uv__udp_recv_start(uv_udp_t* handle,
+                       uv_alloc_cb alloc_cb,
+                       uv_udp_recv_cb recv_cb) {
+  int err;
+
+  if (alloc_cb == NULL || recv_cb == NULL)
+    return UV_EINVAL;
+
+  if (uv__io_active(&handle->io_watcher, POLLIN))
+    return UV_EALREADY;  /* FIXME(bnoordhuis) Should be UV_EBUSY. */
+
+  err = uv__udp_maybe_deferred_bind(handle, AF_INET, 0);
+  if (err)
+    return err;
+
+  handle->alloc_cb = alloc_cb;
+  handle->recv_cb = recv_cb;
+
+  uv__io_start(handle->loop, &handle->io_watcher, POLLIN);
+  uv__handle_start(handle);
+
+  return 0;
+}
+
+
+int uv__udp_recv_stop(uv_udp_t* handle) {
+  uv__io_stop(handle->loop, &handle->io_watcher, POLLIN);
+
+  if (!uv__io_active(&handle->io_watcher, POLLOUT))
+    uv__handle_stop(handle);
+
+  handle->alloc_cb = NULL;
+  handle->recv_cb = NULL;
+
+  return 0;
+}
diff --git a/wpinet/src/main/native/thirdparty/libuv/src/uv-common.cpp b/wpinet/src/main/native/thirdparty/libuv/src/uv-common.cpp
new file mode 100644
index 0000000..8ab600d
--- /dev/null
+++ b/wpinet/src/main/native/thirdparty/libuv/src/uv-common.cpp
@@ -0,0 +1,990 @@
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include "uv.h"
+#include "uv-common.h"
+
+#include <assert.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <stddef.h> /* NULL */
+#include <stdio.h>
+#include <stdlib.h> /* malloc */
+#include <string.h> /* memset */
+
+#if defined(_WIN32)
+# include <malloc.h> /* malloc */
+#else
+# include <net/if.h> /* if_nametoindex */
+# include <sys/un.h> /* AF_UNIX, sockaddr_un */
+#endif
+
+
+typedef struct {
+  uv_malloc_func local_malloc;
+  uv_realloc_func local_realloc;
+  uv_calloc_func local_calloc;
+  uv_free_func local_free;
+} uv__allocator_t;
+
+static uv__allocator_t uv__allocator = {
+  malloc,
+  realloc,
+  calloc,
+  free,
+};
+
+char* uv__strdup(const char* s) {
+  size_t len = strlen(s) + 1;
+  char* m = (char*)uv__malloc(len);
+  if (m == NULL)
+    return NULL;
+  return (char*)memcpy(m, s, len);
+}
+
+char* uv__strndup(const char* s, size_t n) {
+  char* m;
+  size_t len = strlen(s);
+  if (n < len)
+    len = n;
+  m = (char*)uv__malloc(len + 1);
+  if (m == NULL)
+    return NULL;
+  m[len] = '\0';
+  return (char*)memcpy(m, s, len);
+}
+
+void* uv__malloc(size_t size) {
+  if (size > 0)
+    return uv__allocator.local_malloc(size);
+  return NULL;
+}
+
+void uv__free(void* ptr) {
+  int saved_errno;
+
+  /* Libuv expects that free() does not clobber errno.  The system allocator
+   * honors that assumption but custom allocators may not be so careful.
+   */
+  saved_errno = errno;
+  uv__allocator.local_free(ptr);
+  errno = saved_errno;
+}
+
+void* uv__calloc(size_t count, size_t size) {
+  return uv__allocator.local_calloc(count, size);
+}
+
+void* uv__realloc(void* ptr, size_t size) {
+  if (size > 0)
+    return uv__allocator.local_realloc(ptr, size);
+  uv__free(ptr);
+  return NULL;
+}
+
+void* uv__reallocf(void* ptr, size_t size) {
+  void* newptr;
+
+  newptr = uv__realloc(ptr, size);
+  if (newptr == NULL)
+    if (size > 0)
+      uv__free(ptr);
+
+  return newptr;
+}
+
+int uv_replace_allocator(uv_malloc_func malloc_func,
+                         uv_realloc_func realloc_func,
+                         uv_calloc_func calloc_func,
+                         uv_free_func free_func) {
+  if (malloc_func == NULL || realloc_func == NULL ||
+      calloc_func == NULL || free_func == NULL) {
+    return UV_EINVAL;
+  }
+
+  uv__allocator.local_malloc = malloc_func;
+  uv__allocator.local_realloc = realloc_func;
+  uv__allocator.local_calloc = calloc_func;
+  uv__allocator.local_free = free_func;
+
+  return 0;
+}
+
+#define XX(uc, lc) case UV_##uc: return sizeof(uv_##lc##_t);
+
+size_t uv_handle_size(uv_handle_type type) {
+  switch (type) {
+    UV_HANDLE_TYPE_MAP(XX)
+    default:
+      return -1;
+  }
+}
+
+size_t uv_req_size(uv_req_type type) {
+  switch(type) {
+    UV_REQ_TYPE_MAP(XX)
+    default:
+      return -1;
+  }
+}
+
+#undef XX
+
+
+size_t uv_loop_size(void) {
+  return sizeof(uv_loop_t);
+}
+
+
+uv_buf_t uv_buf_init(char* base, unsigned int len) {
+  uv_buf_t buf;
+  buf.base = base;
+  buf.len = len;
+  return buf;
+}
+
+
+static const char* uv__unknown_err_code(int err) {
+  char buf[32];
+  char* copy;
+
+  snprintf(buf, sizeof(buf), "Unknown system error %d", err);
+  copy = uv__strdup(buf);
+
+  return copy != NULL ? copy : "Unknown system error";
+}
+
+#define UV_ERR_NAME_GEN_R(name, _) \
+case UV_## name: \
+  uv__strscpy(buf, #name, buflen); break;
+char* uv_err_name_r(int err, char* buf, size_t buflen) {
+  switch (err) {
+    UV_ERRNO_MAP(UV_ERR_NAME_GEN_R)
+    default: snprintf(buf, buflen, "Unknown system error %d", err);
+  }
+  return buf;
+}
+#undef UV_ERR_NAME_GEN_R
+
+
+#define UV_ERR_NAME_GEN(name, _) case UV_ ## name: return #name;
+const char* uv_err_name(int err) {
+  switch (err) {
+    UV_ERRNO_MAP(UV_ERR_NAME_GEN)
+  }
+  return uv__unknown_err_code(err);
+}
+#undef UV_ERR_NAME_GEN
+
+
+#define UV_STRERROR_GEN_R(name, msg) \
+case UV_ ## name: \
+  snprintf(buf, buflen, "%s", msg); break;
+char* uv_strerror_r(int err, char* buf, size_t buflen) {
+  switch (err) {
+    UV_ERRNO_MAP(UV_STRERROR_GEN_R)
+    default: snprintf(buf, buflen, "Unknown system error %d", err);
+  }
+  return buf;
+}
+#undef UV_STRERROR_GEN_R
+
+
+#define UV_STRERROR_GEN(name, msg) case UV_ ## name: return msg;
+const char* uv_strerror(int err) {
+  switch (err) {
+    UV_ERRNO_MAP(UV_STRERROR_GEN)
+  }
+  return uv__unknown_err_code(err);
+}
+#undef UV_STRERROR_GEN
+
+
+int uv_ip4_addr(const char* ip, int port, struct sockaddr_in* addr) {
+  memset(addr, 0, sizeof(*addr));
+  addr->sin_family = AF_INET;
+  addr->sin_port = htons(port);
+#ifdef SIN6_LEN
+  addr->sin_len = sizeof(*addr);
+#endif
+  return uv_inet_pton(AF_INET, ip, &(addr->sin_addr.s_addr));
+}
+
+
+int uv_ip6_addr(const char* ip, int port, struct sockaddr_in6* addr) {
+  char address_part[40];
+  size_t address_part_size;
+  const char* zone_index;
+
+  memset(addr, 0, sizeof(*addr));
+  addr->sin6_family = AF_INET6;
+  addr->sin6_port = htons(port);
+#ifdef SIN6_LEN
+  addr->sin6_len = sizeof(*addr);
+#endif
+
+  zone_index = strchr(ip, '%');
+  if (zone_index != NULL) {
+    address_part_size = zone_index - ip;
+    if (address_part_size >= sizeof(address_part))
+      address_part_size = sizeof(address_part) - 1;
+
+    memcpy(address_part, ip, address_part_size);
+    address_part[address_part_size] = '\0';
+    ip = address_part;
+
+    zone_index++; /* skip '%' */
+    /* NOTE: unknown interface (id=0) is silently ignored */
+#ifdef _WIN32
+    addr->sin6_scope_id = atoi(zone_index);
+#else
+    addr->sin6_scope_id = if_nametoindex(zone_index);
+#endif
+  }
+
+  return uv_inet_pton(AF_INET6, ip, &addr->sin6_addr);
+}
+
+
+int uv_ip4_name(const struct sockaddr_in* src, char* dst, size_t size) {
+  return uv_inet_ntop(AF_INET, &src->sin_addr, dst, size);
+}
+
+
+int uv_ip6_name(const struct sockaddr_in6* src, char* dst, size_t size) {
+  return uv_inet_ntop(AF_INET6, &src->sin6_addr, dst, size);
+}
+
+
+int uv_ip_name(const struct sockaddr *src, char *dst, size_t size) {
+  switch (src->sa_family) {
+  case AF_INET:
+    return uv_inet_ntop(AF_INET, &((struct sockaddr_in *)src)->sin_addr,
+                        dst, size);
+  case AF_INET6:
+    return uv_inet_ntop(AF_INET6, &((struct sockaddr_in6 *)src)->sin6_addr,
+                        dst, size);
+  default:
+    return UV_EAFNOSUPPORT;
+  }
+}
+
+
+int uv_tcp_bind(uv_tcp_t* handle,
+                const struct sockaddr* addr,
+                unsigned int flags) {
+  unsigned int addrlen;
+
+  if (handle->type != UV_TCP)
+    return UV_EINVAL;
+  if (uv__is_closing(handle)) {
+    return UV_EINVAL;
+  }
+  if (addr->sa_family == AF_INET)
+    addrlen = sizeof(struct sockaddr_in);
+  else if (addr->sa_family == AF_INET6)
+    addrlen = sizeof(struct sockaddr_in6);
+  else
+    return UV_EINVAL;
+
+  return uv__tcp_bind(handle, addr, addrlen, flags);
+}
+
+
+int uv_udp_init_ex(uv_loop_t* loop, uv_udp_t* handle, unsigned flags) {
+  unsigned extra_flags;
+  int domain;
+  int rc;
+
+  /* Use the lower 8 bits for the domain. */
+  domain = flags & 0xFF;
+  if (domain != AF_INET && domain != AF_INET6 && domain != AF_UNSPEC)
+    return UV_EINVAL;
+
+  /* Use the higher bits for extra flags. */
+  extra_flags = flags & ~0xFF;
+  if (extra_flags & ~UV_UDP_RECVMMSG)
+    return UV_EINVAL;
+
+  rc = uv__udp_init_ex(loop, handle, flags, domain);
+
+  if (rc == 0)
+    if (extra_flags & UV_UDP_RECVMMSG)
+      handle->flags |= UV_HANDLE_UDP_RECVMMSG;
+
+  return rc;
+}
+
+
+int uv_udp_init(uv_loop_t* loop, uv_udp_t* handle) {
+  return uv_udp_init_ex(loop, handle, AF_UNSPEC);
+}
+
+
+int uv_udp_bind(uv_udp_t* handle,
+                const struct sockaddr* addr,
+                unsigned int flags) {
+  unsigned int addrlen;
+
+  if (handle->type != UV_UDP)
+    return UV_EINVAL;
+
+  if (addr->sa_family == AF_INET)
+    addrlen = sizeof(struct sockaddr_in);
+  else if (addr->sa_family == AF_INET6)
+    addrlen = sizeof(struct sockaddr_in6);
+  else
+    return UV_EINVAL;
+
+  return uv__udp_bind(handle, addr, addrlen, flags);
+}
+
+
+int uv_tcp_connect(uv_connect_t* req,
+                   uv_tcp_t* handle,
+                   const struct sockaddr* addr,
+                   uv_connect_cb cb) {
+  unsigned int addrlen;
+
+  if (handle->type != UV_TCP)
+    return UV_EINVAL;
+
+  if (addr->sa_family == AF_INET)
+    addrlen = sizeof(struct sockaddr_in);
+  else if (addr->sa_family == AF_INET6)
+    addrlen = sizeof(struct sockaddr_in6);
+  else
+    return UV_EINVAL;
+
+  return uv__tcp_connect(req, handle, addr, addrlen, cb);
+}
+
+
+int uv_udp_connect(uv_udp_t* handle, const struct sockaddr* addr) {
+  unsigned int addrlen;
+
+  if (handle->type != UV_UDP)
+    return UV_EINVAL;
+
+  /* Disconnect the handle */
+  if (addr == NULL) {
+    if (!(handle->flags & UV_HANDLE_UDP_CONNECTED))
+      return UV_ENOTCONN;
+
+    return uv__udp_disconnect(handle);
+  }
+
+  if (addr->sa_family == AF_INET)
+    addrlen = sizeof(struct sockaddr_in);
+  else if (addr->sa_family == AF_INET6)
+    addrlen = sizeof(struct sockaddr_in6);
+  else
+    return UV_EINVAL;
+
+  if (handle->flags & UV_HANDLE_UDP_CONNECTED)
+    return UV_EISCONN;
+
+  return uv__udp_connect(handle, addr, addrlen);
+}
+
+
+int uv__udp_is_connected(uv_udp_t* handle) {
+  struct sockaddr_storage addr;
+  int addrlen;
+  if (handle->type != UV_UDP)
+    return 0;
+
+  addrlen = sizeof(addr);
+  if (uv_udp_getpeername(handle, (struct sockaddr*) &addr, &addrlen) != 0)
+    return 0;
+
+  return addrlen > 0;
+}
+
+
+int uv__udp_check_before_send(uv_udp_t* handle, const struct sockaddr* addr) {
+  unsigned int addrlen;
+
+  if (handle->type != UV_UDP)
+    return UV_EINVAL;
+
+  if (addr != NULL && (handle->flags & UV_HANDLE_UDP_CONNECTED))
+    return UV_EISCONN;
+
+  if (addr == NULL && !(handle->flags & UV_HANDLE_UDP_CONNECTED))
+    return UV_EDESTADDRREQ;
+
+  if (addr != NULL) {
+    if (addr->sa_family == AF_INET)
+      addrlen = sizeof(struct sockaddr_in);
+    else if (addr->sa_family == AF_INET6)
+      addrlen = sizeof(struct sockaddr_in6);
+#if defined(AF_UNIX) && !defined(_WIN32)
+    else if (addr->sa_family == AF_UNIX)
+      addrlen = sizeof(struct sockaddr_un);
+#endif
+    else
+      return UV_EINVAL;
+  } else {
+    addrlen = 0;
+  }
+
+  return addrlen;
+}
+
+
+int uv_udp_send(uv_udp_send_t* req,
+                uv_udp_t* handle,
+                const uv_buf_t bufs[],
+                unsigned int nbufs,
+                const struct sockaddr* addr,
+                uv_udp_send_cb send_cb) {
+  int addrlen;
+
+  addrlen = uv__udp_check_before_send(handle, addr);
+  if (addrlen < 0)
+    return addrlen;
+
+  return uv__udp_send(req, handle, bufs, nbufs, addr, addrlen, send_cb);
+}
+
+
+int uv_udp_try_send(uv_udp_t* handle,
+                    const uv_buf_t bufs[],
+                    unsigned int nbufs,
+                    const struct sockaddr* addr) {
+  int addrlen;
+
+  addrlen = uv__udp_check_before_send(handle, addr);
+  if (addrlen < 0)
+    return addrlen;
+
+  return uv__udp_try_send(handle, bufs, nbufs, addr, addrlen);
+}
+
+
+int uv_udp_recv_start(uv_udp_t* handle,
+                      uv_alloc_cb alloc_cb,
+                      uv_udp_recv_cb recv_cb) {
+  if (handle->type != UV_UDP || alloc_cb == NULL || recv_cb == NULL)
+    return UV_EINVAL;
+  else
+    return uv__udp_recv_start(handle, alloc_cb, recv_cb);
+}
+
+
+int uv_udp_recv_stop(uv_udp_t* handle) {
+  if (handle->type != UV_UDP)
+    return UV_EINVAL;
+  else
+    return uv__udp_recv_stop(handle);
+}
+
+
+void uv_walk(uv_loop_t* loop, uv_walk_cb walk_cb, void* arg) {
+  QUEUE queue;
+  QUEUE* q;
+  uv_handle_t* h;
+
+// FIXME: GCC 12.1 gives a possibly real warning, but we don't know how to fix
+// it
+#if __GNUC__ >= 12
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wdangling-pointer="
+#endif  // __GNUC__ >= 12
+  QUEUE_MOVE(&loop->handle_queue, &queue);
+#if __GNUC__ >= 12
+#pragma GCC diagnostic pop
+#endif  // __GNUC__ >= 12
+  while (!QUEUE_EMPTY(&queue)) {
+    q = QUEUE_HEAD(&queue);
+    h = QUEUE_DATA(q, uv_handle_t, handle_queue);
+
+    QUEUE_REMOVE(q);
+    QUEUE_INSERT_TAIL(&loop->handle_queue, q);
+
+    if (h->flags & UV_HANDLE_INTERNAL) continue;
+    walk_cb(h, arg);
+  }
+}
+
+
+static void uv__print_handles(uv_loop_t* loop, int only_active, FILE* stream) {
+  const char* type;
+  QUEUE* q;
+  uv_handle_t* h;
+
+  if (loop == NULL)
+    loop = uv_default_loop();
+
+  QUEUE_FOREACH(q, &loop->handle_queue) {
+    h = QUEUE_DATA(q, uv_handle_t, handle_queue);
+
+    if (only_active && !uv__is_active(h))
+      continue;
+
+    switch (h->type) {
+#define X(uc, lc) case UV_##uc: type = #lc; break;
+      UV_HANDLE_TYPE_MAP(X)
+#undef X
+      default: type = "<unknown>";
+    }
+
+    fprintf(stream,
+            "[%c%c%c] %-8s %p\n",
+            "R-"[!(h->flags & UV_HANDLE_REF)],
+            "A-"[!(h->flags & UV_HANDLE_ACTIVE)],
+            "I-"[!(h->flags & UV_HANDLE_INTERNAL)],
+            type,
+            (void*)h);
+  }
+}
+
+
+void uv_print_all_handles(uv_loop_t* loop, FILE* stream) {
+  uv__print_handles(loop, 0, stream);
+}
+
+
+void uv_print_active_handles(uv_loop_t* loop, FILE* stream) {
+  uv__print_handles(loop, 1, stream);
+}
+
+
+void uv_ref(uv_handle_t* handle) {
+  uv__handle_ref(handle);
+}
+
+
+void uv_unref(uv_handle_t* handle) {
+  uv__handle_unref(handle);
+}
+
+
+int uv_has_ref(const uv_handle_t* handle) {
+  return uv__has_ref(handle);
+}
+
+
+void uv_stop(uv_loop_t* loop) {
+  loop->stop_flag = 1;
+}
+
+
+uint64_t uv_now(const uv_loop_t* loop) {
+  return loop->time;
+}
+
+
+
+size_t uv__count_bufs(const uv_buf_t bufs[], unsigned int nbufs) {
+  unsigned int i;
+  size_t bytes;
+
+  bytes = 0;
+  for (i = 0; i < nbufs; i++)
+    bytes += (size_t) bufs[i].len;
+
+  return bytes;
+}
+
+int uv_recv_buffer_size(uv_handle_t* handle, int* value) {
+  return uv__socket_sockopt(handle, SO_RCVBUF, value);
+}
+
+int uv_send_buffer_size(uv_handle_t* handle, int *value) {
+  return uv__socket_sockopt(handle, SO_SNDBUF, value);
+}
+
+int uv_fs_event_getpath(uv_fs_event_t* handle, char* buffer, size_t* size) {
+  size_t required_len;
+
+  if (!uv__is_active(handle)) {
+    *size = 0;
+    return UV_EINVAL;
+  }
+
+  required_len = strlen(handle->path);
+  if (required_len >= *size) {
+    *size = required_len + 1;
+    return UV_ENOBUFS;
+  }
+
+  memcpy(buffer, handle->path, required_len);
+  *size = required_len;
+  buffer[required_len] = '\0';
+
+  return 0;
+}
+
+/* The windows implementation does not have the same structure layout as
+ * the unix implementation (nbufs is not directly inside req but is
+ * contained in a nested union/struct) so this function locates it.
+*/
+static unsigned int* uv__get_nbufs(uv_fs_t* req) {
+#ifdef _WIN32
+  return &req->fs.info.nbufs;
+#else
+  return &req->nbufs;
+#endif
+}
+
+/* uv_fs_scandir() uses the system allocator to allocate memory on non-Windows
+ * systems. So, the memory should be released using free(). On Windows,
+ * uv__malloc() is used, so use uv__free() to free memory.
+*/
+#ifdef _WIN32
+# define uv__fs_scandir_free uv__free
+#else
+# define uv__fs_scandir_free free
+#endif
+
+void uv__fs_scandir_cleanup(uv_fs_t* req) {
+  uv__dirent_t** dents;
+
+  unsigned int* nbufs = uv__get_nbufs(req);
+
+  dents = (uv__dirent_t**)(req->ptr);
+  if (*nbufs > 0 && *nbufs != (unsigned int) req->result)
+    (*nbufs)--;
+  for (; *nbufs < (unsigned int) req->result; (*nbufs)++)
+    uv__fs_scandir_free(dents[*nbufs]);
+
+  uv__fs_scandir_free(req->ptr);
+  req->ptr = NULL;
+}
+
+
+int uv_fs_scandir_next(uv_fs_t* req, uv_dirent_t* ent) {
+  uv__dirent_t** dents;
+  uv__dirent_t* dent;
+  unsigned int* nbufs;
+
+  /* Check to see if req passed */
+  if (req->result < 0)
+    return req->result;
+
+  /* Ptr will be null if req was canceled or no files found */
+  if (!req->ptr)
+    return UV_EOF;
+
+  nbufs = uv__get_nbufs(req);
+  assert(nbufs);
+
+  dents = (uv__dirent_t**)(req->ptr);
+
+  /* Free previous entity */
+  if (*nbufs > 0)
+    uv__fs_scandir_free(dents[*nbufs - 1]);
+
+  /* End was already reached */
+  if (*nbufs == (unsigned int) req->result) {
+    uv__fs_scandir_free(dents);
+    req->ptr = NULL;
+    return UV_EOF;
+  }
+
+  dent = dents[(*nbufs)++];
+
+  ent->name = dent->d_name;
+  ent->type = uv__fs_get_dirent_type(dent);
+
+  return 0;
+}
+
+uv_dirent_type_t uv__fs_get_dirent_type(uv__dirent_t* dent) {
+  uv_dirent_type_t type;
+
+#ifdef HAVE_DIRENT_TYPES
+  switch (dent->d_type) {
+    case UV__DT_DIR:
+      type = UV_DIRENT_DIR;
+      break;
+    case UV__DT_FILE:
+      type = UV_DIRENT_FILE;
+      break;
+    case UV__DT_LINK:
+      type = UV_DIRENT_LINK;
+      break;
+    case UV__DT_FIFO:
+      type = UV_DIRENT_FIFO;
+      break;
+    case UV__DT_SOCKET:
+      type = UV_DIRENT_SOCKET;
+      break;
+    case UV__DT_CHAR:
+      type = UV_DIRENT_CHAR;
+      break;
+    case UV__DT_BLOCK:
+      type = UV_DIRENT_BLOCK;
+      break;
+    default:
+      type = UV_DIRENT_UNKNOWN;
+  }
+#else
+  type = UV_DIRENT_UNKNOWN;
+#endif
+
+  return type;
+}
+
+void uv__fs_readdir_cleanup(uv_fs_t* req) {
+  uv_dir_t* dir;
+  uv_dirent_t* dirents;
+  int i;
+
+  if (req->ptr == NULL)
+    return;
+
+  dir = (uv_dir_t*)req->ptr;
+  dirents = dir->dirents;
+  req->ptr = NULL;
+
+  if (dirents == NULL)
+    return;
+
+  for (i = 0; i < req->result; ++i) {
+    uv__free((char*) dirents[i].name);
+    dirents[i].name = NULL;
+  }
+}
+
+
+#ifdef __clang__
+# pragma clang diagnostic push
+# pragma clang diagnostic ignored "-Wvarargs"
+#endif
+
+int uv_loop_configure(uv_loop_t* loop, uv_loop_option option, ...) {
+  va_list ap;
+  int err;
+
+  va_start(ap, option);
+  /* Any platform-agnostic options should be handled here. */
+  err = uv__loop_configure(loop, option, ap);
+  va_end(ap);
+
+  return err;
+}
+
+#ifdef __clang__
+# pragma clang diagnostic pop
+#endif
+
+
+static uv_loop_t default_loop_struct;
+static uv_loop_t* default_loop_ptr;
+
+
+uv_loop_t* uv_default_loop(void) {
+  if (default_loop_ptr != NULL)
+    return default_loop_ptr;
+
+  if (uv_loop_init(&default_loop_struct))
+    return NULL;
+
+  default_loop_ptr = &default_loop_struct;
+  return default_loop_ptr;
+}
+
+
+uv_loop_t* uv_loop_new(void) {
+  uv_loop_t* loop;
+
+  loop = (uv_loop_t*)uv__malloc(sizeof(*loop));
+  if (loop == NULL)
+    return NULL;
+
+  if (uv_loop_init(loop)) {
+    uv__free(loop);
+    return NULL;
+  }
+
+  return loop;
+}
+
+
+int uv_loop_close(uv_loop_t* loop) {
+  QUEUE* q;
+  uv_handle_t* h;
+#ifndef NDEBUG
+  void* saved_data;
+#endif
+
+  if (uv__has_active_reqs(loop))
+    return UV_EBUSY;
+
+  QUEUE_FOREACH(q, &loop->handle_queue) {
+    h = QUEUE_DATA(q, uv_handle_t, handle_queue);
+    if (!(h->flags & UV_HANDLE_INTERNAL))
+      return UV_EBUSY;
+  }
+
+  uv__loop_close(loop);
+
+#ifndef NDEBUG
+  saved_data = loop->data;
+  memset(loop, -1, sizeof(*loop));
+  loop->data = saved_data;
+#endif
+  if (loop == default_loop_ptr)
+    default_loop_ptr = NULL;
+
+  return 0;
+}
+
+
+void uv_loop_delete(uv_loop_t* loop) {
+  uv_loop_t* default_loop;
+  int err;
+
+  default_loop = default_loop_ptr;
+
+  err = uv_loop_close(loop);
+  (void) err;    /* Squelch compiler warnings. */
+  assert(err == 0);
+  if (loop != default_loop)
+    uv__free(loop);
+}
+
+
+int uv_read_start(uv_stream_t* stream,
+                  uv_alloc_cb alloc_cb,
+                  uv_read_cb read_cb) {
+  if (stream == NULL || alloc_cb == NULL || read_cb == NULL)
+    return UV_EINVAL;
+
+  if (stream->flags & UV_HANDLE_CLOSING)
+    return UV_EINVAL;
+
+  if (stream->flags & UV_HANDLE_READING)
+    return UV_EALREADY;
+
+  if (!(stream->flags & UV_HANDLE_READABLE))
+    return UV_ENOTCONN;
+
+  return uv__read_start(stream, alloc_cb, read_cb);
+}
+
+
+void uv_os_free_environ(uv_env_item_t* envitems, int count) {
+  int i;
+
+  for (i = 0; i < count; i++) {
+    uv__free(envitems[i].name);
+  }
+
+  uv__free(envitems);
+}
+
+
+void uv_free_cpu_info(uv_cpu_info_t* cpu_infos, int count) {
+  int i;
+
+  for (i = 0; i < count; i++)
+    uv__free(cpu_infos[i].model);
+
+  uv__free(cpu_infos);
+}
+
+
+/* Also covers __clang__ and __INTEL_COMPILER. Disabled on Windows because
+ * threads have already been forcibly terminated by the operating system
+ * by the time destructors run, ergo, it's not safe to try to clean them up.
+ */
+#if defined(__GNUC__) && !defined(_WIN32)
+__attribute__((destructor))
+#endif
+void uv_library_shutdown(void) {
+  static int was_shutdown;
+
+  if (uv__load_relaxed(&was_shutdown))
+    return;
+
+  uv__process_title_cleanup();
+  uv__signal_cleanup();
+#ifdef __MVS__
+  /* TODO(itodorov) - zos: revisit when Woz compiler is available. */
+  uv__os390_cleanup();
+#else
+  uv__threadpool_cleanup();
+#endif
+  uv__store_relaxed(&was_shutdown, 1);
+}
+
+
+void uv__metrics_update_idle_time(uv_loop_t* loop) {
+  uv__loop_metrics_t* loop_metrics;
+  uint64_t entry_time;
+  uint64_t exit_time;
+
+  if (!(uv__get_internal_fields(loop)->flags & UV_METRICS_IDLE_TIME))
+    return;
+
+  loop_metrics = uv__get_loop_metrics(loop);
+
+  /* The thread running uv__metrics_update_idle_time() is always the same
+   * thread that sets provider_entry_time. So it's unnecessary to lock before
+   * retrieving this value.
+   */
+  if (loop_metrics->provider_entry_time == 0)
+    return;
+
+  exit_time = uv_hrtime();
+
+  uv_mutex_lock(&loop_metrics->lock);
+  entry_time = loop_metrics->provider_entry_time;
+  loop_metrics->provider_entry_time = 0;
+  loop_metrics->provider_idle_time += exit_time - entry_time;
+  uv_mutex_unlock(&loop_metrics->lock);
+}
+
+
+void uv__metrics_set_provider_entry_time(uv_loop_t* loop) {
+  uv__loop_metrics_t* loop_metrics;
+  uint64_t now;
+
+  if (!(uv__get_internal_fields(loop)->flags & UV_METRICS_IDLE_TIME))
+    return;
+
+  now = uv_hrtime();
+  loop_metrics = uv__get_loop_metrics(loop);
+  uv_mutex_lock(&loop_metrics->lock);
+  loop_metrics->provider_entry_time = now;
+  uv_mutex_unlock(&loop_metrics->lock);
+}
+
+
+uint64_t uv_metrics_idle_time(uv_loop_t* loop) {
+  uv__loop_metrics_t* loop_metrics;
+  uint64_t entry_time;
+  uint64_t idle_time;
+
+  loop_metrics = uv__get_loop_metrics(loop);
+  uv_mutex_lock(&loop_metrics->lock);
+  idle_time = loop_metrics->provider_idle_time;
+  entry_time = loop_metrics->provider_entry_time;
+  uv_mutex_unlock(&loop_metrics->lock);
+
+  if (entry_time > 0)
+    idle_time += uv_hrtime() - entry_time;
+  return idle_time;
+}
diff --git a/wpinet/src/main/native/thirdparty/libuv/src/uv-common.h b/wpinet/src/main/native/thirdparty/libuv/src/uv-common.h
new file mode 100644
index 0000000..6001b0c
--- /dev/null
+++ b/wpinet/src/main/native/thirdparty/libuv/src/uv-common.h
@@ -0,0 +1,376 @@
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+/*
+ * This file is private to libuv. It provides common functionality to both
+ * Windows and Unix backends.
+ */
+
+#ifndef UV_COMMON_H_
+#define UV_COMMON_H_
+
+#include <assert.h>
+#include <stdarg.h>
+#include <stddef.h>
+
+#if defined(_MSC_VER) && _MSC_VER < 1600
+# include "uv/stdint-msvc2008.h"
+#else
+# include <stdint.h>
+#endif
+
+#include "uv.h"
+#include "uv/tree.h"
+#include "queue.h"
+#include "strscpy.h"
+
+#if EDOM > 0
+# define UV__ERR(x) (-(x))
+#else
+# define UV__ERR(x) (x)
+#endif
+
+#if !defined(snprintf) && defined(_MSC_VER) && _MSC_VER < 1900
+extern int snprintf(char*, size_t, const char*, ...);
+#endif
+
+#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
+
+#define container_of(ptr, type, member) \
+  ((type *) ((char *) (ptr) - offsetof(type, member)))
+
+#define STATIC_ASSERT(expr)                                                   \
+  void uv__static_assert(int static_assert_failed[1 - 2 * !(expr)])
+
+#if defined(__GNUC__) && (__GNUC__ > 4 || __GNUC__ == 4 && __GNUC_MINOR__ >= 7)
+#define uv__load_relaxed(p) __atomic_load_n(p, __ATOMIC_RELAXED)
+#define uv__store_relaxed(p, v) __atomic_store_n(p, v, __ATOMIC_RELAXED)
+#else
+#define uv__load_relaxed(p) (*p)
+#define uv__store_relaxed(p, v) do *p = v; while (0)
+#endif
+
+#define UV__UDP_DGRAM_MAXSIZE (64 * 1024)
+
+/* Handle flags. Some flags are specific to Windows or UNIX. */
+enum {
+  /* Used by all handles. */
+  UV_HANDLE_CLOSING                     = 0x00000001,
+  UV_HANDLE_CLOSED                      = 0x00000002,
+  UV_HANDLE_ACTIVE                      = 0x00000004,
+  UV_HANDLE_REF                         = 0x00000008,
+  UV_HANDLE_INTERNAL                    = 0x00000010,
+  UV_HANDLE_ENDGAME_QUEUED              = 0x00000020,
+
+  /* Used by streams. */
+  UV_HANDLE_LISTENING                   = 0x00000040,
+  UV_HANDLE_CONNECTION                  = 0x00000080,
+  UV_HANDLE_SHUTTING                    = 0x00000100,
+  UV_HANDLE_SHUT                        = 0x00000200,
+  UV_HANDLE_READ_PARTIAL                = 0x00000400,
+  UV_HANDLE_READ_EOF                    = 0x00000800,
+
+  /* Used by streams and UDP handles. */
+  UV_HANDLE_READING                     = 0x00001000,
+  UV_HANDLE_BOUND                       = 0x00002000,
+  UV_HANDLE_READABLE                    = 0x00004000,
+  UV_HANDLE_WRITABLE                    = 0x00008000,
+  UV_HANDLE_READ_PENDING                = 0x00010000,
+  UV_HANDLE_SYNC_BYPASS_IOCP            = 0x00020000,
+  UV_HANDLE_ZERO_READ                   = 0x00040000,
+  UV_HANDLE_EMULATE_IOCP                = 0x00080000,
+  UV_HANDLE_BLOCKING_WRITES             = 0x00100000,
+  UV_HANDLE_CANCELLATION_PENDING        = 0x00200000,
+
+  /* Used by uv_tcp_t and uv_udp_t handles */
+  UV_HANDLE_IPV6                        = 0x00400000,
+
+  /* Only used by uv_tcp_t handles. */
+  UV_HANDLE_TCP_NODELAY                 = 0x01000000,
+  UV_HANDLE_TCP_KEEPALIVE               = 0x02000000,
+  UV_HANDLE_TCP_SINGLE_ACCEPT           = 0x04000000,
+  UV_HANDLE_TCP_ACCEPT_STATE_CHANGING   = 0x08000000,
+  UV_HANDLE_SHARED_TCP_SOCKET           = 0x10000000,
+
+  /* Only used by uv_udp_t handles. */
+  UV_HANDLE_UDP_PROCESSING              = 0x01000000,
+  UV_HANDLE_UDP_CONNECTED               = 0x02000000,
+  UV_HANDLE_UDP_RECVMMSG                = 0x04000000,
+
+  /* Only used by uv_pipe_t handles. */
+  UV_HANDLE_NON_OVERLAPPED_PIPE         = 0x01000000,
+  UV_HANDLE_PIPESERVER                  = 0x02000000,
+
+  /* Only used by uv_tty_t handles. */
+  UV_HANDLE_TTY_READABLE                = 0x01000000,
+  UV_HANDLE_TTY_RAW                     = 0x02000000,
+  UV_HANDLE_TTY_SAVED_POSITION          = 0x04000000,
+  UV_HANDLE_TTY_SAVED_ATTRIBUTES        = 0x08000000,
+
+  /* Only used by uv_signal_t handles. */
+  UV_SIGNAL_ONE_SHOT_DISPATCHED         = 0x01000000,
+  UV_SIGNAL_ONE_SHOT                    = 0x02000000,
+
+  /* Only used by uv_poll_t handles. */
+  UV_HANDLE_POLL_SLOW                   = 0x01000000,
+
+  /* Only used by uv_process_t handles. */
+  UV_HANDLE_REAP                        = 0x10000000
+};
+
+int uv__loop_configure(uv_loop_t* loop, uv_loop_option option, va_list ap);
+
+void uv__loop_close(uv_loop_t* loop);
+
+int uv__read_start(uv_stream_t* stream,
+                   uv_alloc_cb alloc_cb,
+                   uv_read_cb read_cb);
+
+int uv__tcp_bind(uv_tcp_t* tcp,
+                 const struct sockaddr* addr,
+                 unsigned int addrlen,
+                 unsigned int flags);
+
+int uv__tcp_connect(uv_connect_t* req,
+                   uv_tcp_t* handle,
+                   const struct sockaddr* addr,
+                   unsigned int addrlen,
+                   uv_connect_cb cb);
+
+int uv__udp_init_ex(uv_loop_t* loop,
+                    uv_udp_t* handle,
+                    unsigned flags,
+                    int domain);
+
+int uv__udp_bind(uv_udp_t* handle,
+                 const struct sockaddr* addr,
+                 unsigned int  addrlen,
+                 unsigned int flags);
+
+int uv__udp_connect(uv_udp_t* handle,
+                    const struct sockaddr* addr,
+                    unsigned int addrlen);
+
+int uv__udp_disconnect(uv_udp_t* handle);
+
+int uv__udp_is_connected(uv_udp_t* handle);
+
+int uv__udp_send(uv_udp_send_t* req,
+                 uv_udp_t* handle,
+                 const uv_buf_t bufs[],
+                 unsigned int nbufs,
+                 const struct sockaddr* addr,
+                 unsigned int addrlen,
+                 uv_udp_send_cb send_cb);
+
+int uv__udp_try_send(uv_udp_t* handle,
+                     const uv_buf_t bufs[],
+                     unsigned int nbufs,
+                     const struct sockaddr* addr,
+                     unsigned int addrlen);
+
+int uv__udp_recv_start(uv_udp_t* handle, uv_alloc_cb alloccb,
+                       uv_udp_recv_cb recv_cb);
+
+int uv__udp_recv_stop(uv_udp_t* handle);
+
+void uv__fs_poll_close(uv_fs_poll_t* handle);
+
+int uv__getaddrinfo_translate_error(int sys_err);    /* EAI_* error. */
+
+enum uv__work_kind {
+  UV__WORK_CPU,
+  UV__WORK_FAST_IO,
+  UV__WORK_SLOW_IO
+};
+
+void uv__work_submit(uv_loop_t* loop,
+                     struct uv__work *w,
+                     enum uv__work_kind kind,
+                     void (*work)(struct uv__work *w),
+                     void (*done)(struct uv__work *w, int status));
+
+void uv__work_done(uv_async_t* handle);
+
+size_t uv__count_bufs(const uv_buf_t bufs[], unsigned int nbufs);
+
+int uv__socket_sockopt(uv_handle_t* handle, int optname, int* value);
+
+void uv__fs_scandir_cleanup(uv_fs_t* req);
+void uv__fs_readdir_cleanup(uv_fs_t* req);
+uv_dirent_type_t uv__fs_get_dirent_type(uv__dirent_t* dent);
+
+int uv__next_timeout(const uv_loop_t* loop);
+void uv__run_timers(uv_loop_t* loop);
+void uv__timer_close(uv_timer_t* handle);
+
+void uv__process_title_cleanup(void);
+void uv__signal_cleanup(void);
+void uv__threadpool_cleanup(void);
+
+#define uv__has_active_reqs(loop)                                             \
+  ((loop)->active_reqs.count > 0)
+
+#define uv__req_register(loop, req)                                           \
+  do {                                                                        \
+    (loop)->active_reqs.count++;                                              \
+  }                                                                           \
+  while (0)
+
+#define uv__req_unregister(loop, req)                                         \
+  do {                                                                        \
+    assert(uv__has_active_reqs(loop));                                        \
+    (loop)->active_reqs.count--;                                              \
+  }                                                                           \
+  while (0)
+
+#define uv__has_active_handles(loop)                                          \
+  ((loop)->active_handles > 0)
+
+#define uv__active_handle_add(h)                                              \
+  do {                                                                        \
+    (h)->loop->active_handles++;                                              \
+  }                                                                           \
+  while (0)
+
+#define uv__active_handle_rm(h)                                               \
+  do {                                                                        \
+    (h)->loop->active_handles--;                                              \
+  }                                                                           \
+  while (0)
+
+#define uv__is_active(h)                                                      \
+  (((h)->flags & UV_HANDLE_ACTIVE) != 0)
+
+#define uv__is_closing(h)                                                     \
+  (((h)->flags & (UV_HANDLE_CLOSING | UV_HANDLE_CLOSED)) != 0)
+
+#define uv__handle_start(h)                                                   \
+  do {                                                                        \
+    if (((h)->flags & UV_HANDLE_ACTIVE) != 0) break;                          \
+    (h)->flags |= UV_HANDLE_ACTIVE;                                           \
+    if (((h)->flags & UV_HANDLE_REF) != 0) uv__active_handle_add(h);          \
+  }                                                                           \
+  while (0)
+
+#define uv__handle_stop(h)                                                    \
+  do {                                                                        \
+    if (((h)->flags & UV_HANDLE_ACTIVE) == 0) break;                          \
+    (h)->flags &= ~UV_HANDLE_ACTIVE;                                          \
+    if (((h)->flags & UV_HANDLE_REF) != 0) uv__active_handle_rm(h);           \
+  }                                                                           \
+  while (0)
+
+#define uv__handle_ref(h)                                                     \
+  do {                                                                        \
+    if (((h)->flags & UV_HANDLE_REF) != 0) break;                             \
+    (h)->flags |= UV_HANDLE_REF;                                              \
+    if (((h)->flags & UV_HANDLE_CLOSING) != 0) break;                         \
+    if (((h)->flags & UV_HANDLE_ACTIVE) != 0) uv__active_handle_add(h);       \
+  }                                                                           \
+  while (0)
+
+#define uv__handle_unref(h)                                                   \
+  do {                                                                        \
+    if (((h)->flags & UV_HANDLE_REF) == 0) break;                             \
+    (h)->flags &= ~UV_HANDLE_REF;                                             \
+    if (((h)->flags & UV_HANDLE_CLOSING) != 0) break;                         \
+    if (((h)->flags & UV_HANDLE_ACTIVE) != 0) uv__active_handle_rm(h);        \
+  }                                                                           \
+  while (0)
+
+#define uv__has_ref(h)                                                        \
+  (((h)->flags & UV_HANDLE_REF) != 0)
+
+#if defined(_WIN32)
+# define uv__handle_platform_init(h) ((h)->u.fd = -1)
+#else
+# define uv__handle_platform_init(h) ((h)->next_closing = NULL)
+#endif
+
+#define uv__handle_init(loop_, h, type_)                                      \
+  do {                                                                        \
+    (h)->loop = (loop_);                                                      \
+    (h)->type = (type_);                                                      \
+    (h)->flags = UV_HANDLE_REF;  /* Ref the loop when active. */              \
+    QUEUE_INSERT_TAIL(&(loop_)->handle_queue, &(h)->handle_queue);            \
+    uv__handle_platform_init(h);                                              \
+  }                                                                           \
+  while (0)
+
+/* Note: uses an open-coded version of SET_REQ_SUCCESS() because of
+ * a circular dependency between src/uv-common.h and src/win/internal.h.
+ */
+#if defined(_WIN32)
+# define UV_REQ_INIT(req, typ)                                                \
+  do {                                                                        \
+    (req)->type = (typ);                                                      \
+    (req)->u.io.overlapped.Internal = 0;  /* SET_REQ_SUCCESS() */             \
+  }                                                                           \
+  while (0)
+#else
+# define UV_REQ_INIT(req, typ)                                                \
+  do {                                                                        \
+    (req)->type = (typ);                                                      \
+  }                                                                           \
+  while (0)
+#endif
+
+#define uv__req_init(loop, req, typ)                                          \
+  do {                                                                        \
+    UV_REQ_INIT(req, typ);                                                    \
+    uv__req_register(loop, req);                                              \
+  }                                                                           \
+  while (0)
+
+#define uv__get_internal_fields(loop)                                         \
+  ((uv__loop_internal_fields_t*) loop->internal_fields)
+
+#define uv__get_loop_metrics(loop)                                            \
+  (&uv__get_internal_fields(loop)->loop_metrics)
+
+/* Allocator prototypes */
+void *uv__calloc(size_t count, size_t size);
+char *uv__strdup(const char* s);
+char *uv__strndup(const char* s, size_t n);
+void* uv__malloc(size_t size);
+void uv__free(void* ptr);
+void* uv__realloc(void* ptr, size_t size);
+void* uv__reallocf(void* ptr, size_t size);
+
+typedef struct uv__loop_metrics_s uv__loop_metrics_t;
+typedef struct uv__loop_internal_fields_s uv__loop_internal_fields_t;
+
+struct uv__loop_metrics_s {
+  uint64_t provider_entry_time;
+  uint64_t provider_idle_time;
+  uv_mutex_t lock;
+};
+
+void uv__metrics_update_idle_time(uv_loop_t* loop);
+void uv__metrics_set_provider_entry_time(uv_loop_t* loop);
+
+struct uv__loop_internal_fields_s {
+  unsigned int flags;
+  uv__loop_metrics_t loop_metrics;
+};
+
+#endif /* UV_COMMON_H_ */
diff --git a/wpinet/src/main/native/thirdparty/libuv/src/uv-data-getter-setters.cpp b/wpinet/src/main/native/thirdparty/libuv/src/uv-data-getter-setters.cpp
new file mode 100644
index 0000000..0bd0448
--- /dev/null
+++ b/wpinet/src/main/native/thirdparty/libuv/src/uv-data-getter-setters.cpp
@@ -0,0 +1,119 @@
+/* Copyright libuv project contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include "uv.h"
+
+const char* uv_handle_type_name(uv_handle_type type) {
+  switch (type) {
+#define XX(uc,lc) case UV_##uc: return #lc;
+  UV_HANDLE_TYPE_MAP(XX)
+#undef XX
+  case UV_FILE: return "file";
+  case UV_HANDLE_TYPE_MAX:
+  case UV_UNKNOWN_HANDLE: return NULL;
+  }
+  return NULL;
+}
+
+uv_handle_type uv_handle_get_type(const uv_handle_t* handle) {
+  return handle->type;
+}
+
+void* uv_handle_get_data(const uv_handle_t* handle) {
+  return handle->data;
+}
+
+uv_loop_t* uv_handle_get_loop(const uv_handle_t* handle) {
+  return handle->loop;
+}
+
+void uv_handle_set_data(uv_handle_t* handle, void* data) {
+  handle->data = data;
+}
+
+const char* uv_req_type_name(uv_req_type type) {
+  switch (type) {
+#define XX(uc,lc) case UV_##uc: return #lc;
+  UV_REQ_TYPE_MAP(XX)
+#undef XX
+  case UV_REQ_TYPE_MAX:
+  case UV_UNKNOWN_REQ:
+  default: /* UV_REQ_TYPE_PRIVATE */
+    break;
+  }
+  return NULL;
+}
+
+uv_req_type uv_req_get_type(const uv_req_t* req) {
+  return req->type;
+}
+
+void* uv_req_get_data(const uv_req_t* req) {
+  return req->data;
+}
+
+void uv_req_set_data(uv_req_t* req, void* data) {
+  req->data = data;
+}
+
+size_t uv_stream_get_write_queue_size(const uv_stream_t* stream) {
+  return stream->write_queue_size;
+}
+
+size_t uv_udp_get_send_queue_size(const uv_udp_t* handle) {
+  return handle->send_queue_size;
+}
+
+size_t uv_udp_get_send_queue_count(const uv_udp_t* handle) {
+  return handle->send_queue_count;
+}
+
+uv_pid_t uv_process_get_pid(const uv_process_t* proc) {
+  return proc->pid;
+}
+
+uv_fs_type uv_fs_get_type(const uv_fs_t* req) {
+  return req->fs_type;
+}
+
+ssize_t uv_fs_get_result(const uv_fs_t* req) {
+  return req->result;
+}
+
+void* uv_fs_get_ptr(const uv_fs_t* req) {
+  return req->ptr;
+}
+
+const char* uv_fs_get_path(const uv_fs_t* req) {
+  return req->path;
+}
+
+uv_stat_t* uv_fs_get_statbuf(uv_fs_t* req) {
+  return &req->statbuf;
+}
+
+void* uv_loop_get_data(const uv_loop_t* loop) {
+  return loop->data;
+}
+
+void uv_loop_set_data(uv_loop_t* loop, void* data) {
+  loop->data = data;
+}
diff --git a/wpinet/src/main/native/thirdparty/libuv/src/version.cpp b/wpinet/src/main/native/thirdparty/libuv/src/version.cpp
new file mode 100644
index 0000000..686dedd
--- /dev/null
+++ b/wpinet/src/main/native/thirdparty/libuv/src/version.cpp
@@ -0,0 +1,45 @@
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include "uv.h"
+
+#define UV_STRINGIFY(v) UV_STRINGIFY_HELPER(v)
+#define UV_STRINGIFY_HELPER(v) #v
+
+#define UV_VERSION_STRING_BASE  UV_STRINGIFY(UV_VERSION_MAJOR) "." \
+                                UV_STRINGIFY(UV_VERSION_MINOR) "." \
+                                UV_STRINGIFY(UV_VERSION_PATCH)
+
+#if UV_VERSION_IS_RELEASE
+# define UV_VERSION_STRING  UV_VERSION_STRING_BASE
+#else
+# define UV_VERSION_STRING  UV_VERSION_STRING_BASE "-" UV_VERSION_SUFFIX
+#endif
+
+
+unsigned int uv_version(void) {
+  return UV_VERSION_HEX;
+}
+
+
+const char* uv_version_string(void) {
+  return UV_VERSION_STRING;
+}
diff --git a/wpinet/src/main/native/thirdparty/libuv/src/win/async.cpp b/wpinet/src/main/native/thirdparty/libuv/src/win/async.cpp
new file mode 100644
index 0000000..b904676
--- /dev/null
+++ b/wpinet/src/main/native/thirdparty/libuv/src/win/async.cpp
@@ -0,0 +1,98 @@
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include <assert.h>
+
+#include "uv.h"
+#include "internal.h"
+#include "atomicops-inl.h"
+#include "handle-inl.h"
+#include "req-inl.h"
+
+
+void uv__async_endgame(uv_loop_t* loop, uv_async_t* handle) {
+  if (handle->flags & UV_HANDLE_CLOSING &&
+      !handle->async_sent) {
+    assert(!(handle->flags & UV_HANDLE_CLOSED));
+    uv__handle_close(handle);
+  }
+}
+
+
+int uv_async_init(uv_loop_t* loop, uv_async_t* handle, uv_async_cb async_cb) {
+  uv_req_t* req;
+
+  uv__handle_init(loop, (uv_handle_t*) handle, UV_ASYNC);
+  handle->async_sent = 0;
+  handle->async_cb = async_cb;
+
+  req = &handle->async_req;
+  UV_REQ_INIT(req, UV_WAKEUP);
+  req->data = handle;
+
+  uv__handle_start(handle);
+
+  return 0;
+}
+
+
+void uv__async_close(uv_loop_t* loop, uv_async_t* handle) {
+  if (!((uv_async_t*)handle)->async_sent) {
+    uv__want_endgame(loop, (uv_handle_t*) handle);
+  }
+
+  uv__handle_closing(handle);
+}
+
+
+int uv_async_send(uv_async_t* handle) {
+  uv_loop_t* loop = handle->loop;
+
+  if (handle->type != UV_ASYNC) {
+    /* Can't set errno because that's not thread-safe. */
+    return -1;
+  }
+
+  /* The user should make sure never to call uv_async_send to a closing or
+   * closed handle. */
+  assert(!(handle->flags & UV_HANDLE_CLOSING));
+
+  if (!uv__atomic_exchange_set(&handle->async_sent)) {
+    POST_COMPLETION_FOR_REQ(loop, &handle->async_req);
+  }
+
+  return 0;
+}
+
+
+void uv__process_async_wakeup_req(uv_loop_t* loop, uv_async_t* handle,
+    uv_req_t* req) {
+  assert(handle->type == UV_ASYNC);
+  assert(req->type == UV_WAKEUP);
+
+  handle->async_sent = 0;
+
+  if (handle->flags & UV_HANDLE_CLOSING) {
+    uv__want_endgame(loop, (uv_handle_t*)handle);
+  } else if (handle->async_cb != NULL) {
+    handle->async_cb(handle);
+  }
+}
diff --git a/wpinet/src/main/native/thirdparty/libuv/src/win/atomicops-inl.h b/wpinet/src/main/native/thirdparty/libuv/src/win/atomicops-inl.h
new file mode 100644
index 0000000..2f984c6
--- /dev/null
+++ b/wpinet/src/main/native/thirdparty/libuv/src/win/atomicops-inl.h
@@ -0,0 +1,61 @@
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#ifndef UV_WIN_ATOMICOPS_INL_H_
+#define UV_WIN_ATOMICOPS_INL_H_
+
+#include "uv.h"
+#include "internal.h"
+
+
+/* Atomic set operation on char */
+#ifdef _MSC_VER /* MSVC */
+
+/* _InterlockedOr8 is supported by MSVC on x32 and x64. It is slightly less
+ * efficient than InterlockedExchange, but InterlockedExchange8 does not exist,
+ * and interlocked operations on larger targets might require the target to be
+ * aligned. */
+#pragma intrinsic(_InterlockedOr8)
+
+static char INLINE uv__atomic_exchange_set(char volatile* target) {
+  return _InterlockedOr8(target, 1);
+}
+
+#else /* GCC, Clang in mingw mode */
+
+static inline char uv__atomic_exchange_set(char volatile* target) {
+#if defined(__i386__) || defined(__x86_64__)
+  /* Mingw-32 version, hopefully this works for 64-bit gcc as well. */
+  const char one = 1;
+  char old_value;
+  __asm__ __volatile__ ("lock xchgb %0, %1\n\t"
+                        : "=r"(old_value), "=m"(*target)
+                        : "0"(one), "m"(*target)
+                        : "memory");
+  return old_value;
+#else
+  return __sync_fetch_and_or(target, 1);
+#endif
+}
+
+#endif
+
+#endif /* UV_WIN_ATOMICOPS_INL_H_ */
diff --git a/wpinet/src/main/native/thirdparty/libuv/src/win/core.cpp b/wpinet/src/main/native/thirdparty/libuv/src/win/core.cpp
new file mode 100644
index 0000000..0752edf
--- /dev/null
+++ b/wpinet/src/main/native/thirdparty/libuv/src/win/core.cpp
@@ -0,0 +1,754 @@
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include <assert.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#if defined(_MSC_VER) || defined(__MINGW64_VERSION_MAJOR)
+#include <crtdbg.h>
+#endif
+
+#include "uv.h"
+#include "internal.h"
+#include "queue.h"
+#include "handle-inl.h"
+#include "heap-inl.h"
+#include "req-inl.h"
+
+/* uv_once initialization guards */
+static uv_once_t uv_init_guard_ = UV_ONCE_INIT;
+
+
+#if defined(_DEBUG) && (defined(_MSC_VER) || defined(__MINGW64_VERSION_MAJOR))
+/* Our crt debug report handler allows us to temporarily disable asserts
+ * just for the current thread.
+ */
+
+UV_THREAD_LOCAL int uv__crt_assert_enabled = TRUE;
+
+static int uv__crt_dbg_report_handler(int report_type, char *message, int *ret_val) {
+  if (uv__crt_assert_enabled || report_type != _CRT_ASSERT)
+    return FALSE;
+
+  if (ret_val) {
+    /* Set ret_val to 0 to continue with normal execution.
+     * Set ret_val to 1 to trigger a breakpoint.
+    */
+
+    if(IsDebuggerPresent())
+      *ret_val = 1;
+    else
+      *ret_val = 0;
+  }
+
+  /* Don't call _CrtDbgReport. */
+  return TRUE;
+}
+#else
+UV_THREAD_LOCAL int uv__crt_assert_enabled = FALSE;
+#endif
+
+
+#if !defined(__MINGW32__) || __MSVCRT_VERSION__ >= 0x800
+static void uv__crt_invalid_parameter_handler(const wchar_t* expression,
+    const wchar_t* function, const wchar_t * file, unsigned int line,
+    uintptr_t reserved) {
+  /* No-op. */
+}
+#endif
+
+static uv_loop_t** uv__loops;
+static int uv__loops_size;
+static int uv__loops_capacity;
+#define UV__LOOPS_CHUNK_SIZE 8
+static uv_mutex_t uv__loops_lock;
+
+
+static void uv__loops_init(void) {
+  uv_mutex_init(&uv__loops_lock);
+}
+
+
+static int uv__loops_add(uv_loop_t* loop) {
+  uv_loop_t** new_loops;
+  int new_capacity, i;
+
+  uv_mutex_lock(&uv__loops_lock);
+
+  if (uv__loops_size == uv__loops_capacity) {
+    new_capacity = uv__loops_capacity + UV__LOOPS_CHUNK_SIZE;
+    new_loops = (uv_loop_t**)
+        uv__realloc(uv__loops, sizeof(uv_loop_t*) * new_capacity);
+    if (!new_loops)
+      goto failed_loops_realloc;
+    uv__loops = new_loops;
+    for (i = uv__loops_capacity; i < new_capacity; ++i)
+      uv__loops[i] = NULL;
+    uv__loops_capacity = new_capacity;
+  }
+  uv__loops[uv__loops_size] = loop;
+  ++uv__loops_size;
+
+  uv_mutex_unlock(&uv__loops_lock);
+  return 0;
+
+failed_loops_realloc:
+  uv_mutex_unlock(&uv__loops_lock);
+  return ERROR_OUTOFMEMORY;
+}
+
+
+static void uv__loops_remove(uv_loop_t* loop) {
+  int loop_index;
+  int smaller_capacity;
+  uv_loop_t** new_loops;
+
+  uv_mutex_lock(&uv__loops_lock);
+
+  for (loop_index = 0; loop_index < uv__loops_size; ++loop_index) {
+    if (uv__loops[loop_index] == loop)
+      break;
+  }
+  /* If loop was not found, ignore */
+  if (loop_index == uv__loops_size)
+    goto loop_removed;
+
+  uv__loops[loop_index] = uv__loops[uv__loops_size - 1];
+  uv__loops[uv__loops_size - 1] = NULL;
+  --uv__loops_size;
+
+  if (uv__loops_size == 0) {
+    uv__loops_capacity = 0;
+    uv__free(uv__loops);
+    uv__loops = NULL;
+    goto loop_removed;
+  }
+
+  /* If we didn't grow to big skip downsizing */
+  if (uv__loops_capacity < 4 * UV__LOOPS_CHUNK_SIZE)
+    goto loop_removed;
+
+  /* Downsize only if more than half of buffer is free */
+  smaller_capacity = uv__loops_capacity / 2;
+  if (uv__loops_size >= smaller_capacity)
+    goto loop_removed;
+  new_loops = (uv_loop_t**)
+      uv__realloc(uv__loops, sizeof(uv_loop_t*) * smaller_capacity);
+  if (!new_loops)
+    goto loop_removed;
+  uv__loops = new_loops;
+  uv__loops_capacity = smaller_capacity;
+
+loop_removed:
+  uv_mutex_unlock(&uv__loops_lock);
+}
+
+void uv__wake_all_loops(void) {
+  int i;
+  uv_loop_t* loop;
+
+  uv_mutex_lock(&uv__loops_lock);
+  for (i = 0; i < uv__loops_size; ++i) {
+    loop = uv__loops[i];
+    assert(loop);
+    if (loop->iocp != INVALID_HANDLE_VALUE)
+      PostQueuedCompletionStatus(loop->iocp, 0, 0, NULL);
+  }
+  uv_mutex_unlock(&uv__loops_lock);
+}
+
+static void uv__init(void) {
+  /* Tell Windows that we will handle critical errors. */
+  SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX |
+               SEM_NOOPENFILEERRORBOX);
+
+  /* Tell the CRT to not exit the application when an invalid parameter is
+   * passed. The main issue is that invalid FDs will trigger this behavior.
+   */
+#if !defined(__MINGW32__) || __MSVCRT_VERSION__ >= 0x800
+  _set_invalid_parameter_handler(uv__crt_invalid_parameter_handler);
+#endif
+
+  /* We also need to setup our debug report handler because some CRT
+   * functions (eg _get_osfhandle) raise an assert when called with invalid
+   * FDs even though they return the proper error code in the release build.
+   */
+#if defined(_DEBUG) && (defined(_MSC_VER) || defined(__MINGW64_VERSION_MAJOR))
+  _CrtSetReportHook(uv__crt_dbg_report_handler);
+#endif
+
+  /* Initialize tracking of all uv loops */
+  uv__loops_init();
+
+  /* Fetch winapi function pointers. This must be done first because other
+   * initialization code might need these function pointers to be loaded.
+   */
+  uv__winapi_init();
+
+  /* Initialize winsock */
+  uv__winsock_init();
+
+  /* Initialize FS */
+  uv__fs_init();
+
+  /* Initialize signal stuff */
+  uv__signals_init();
+
+  /* Initialize console */
+  uv__console_init();
+
+  /* Initialize utilities */
+  uv__util_init();
+
+  /* Initialize system wakeup detection */
+  uv__init_detect_system_wakeup();
+}
+
+
+int uv_loop_init(uv_loop_t* loop) {
+  uv__loop_internal_fields_t* lfields;
+  struct heap* timer_heap;
+  int err;
+
+  /* Initialize libuv itself first */
+  uv__once_init();
+
+  /* Create an I/O completion port */
+  loop->iocp = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 1);
+  if (loop->iocp == NULL)
+    return uv_translate_sys_error(GetLastError());
+
+  lfields = (uv__loop_internal_fields_t*) uv__calloc(1, sizeof(*lfields));
+  if (lfields == NULL)
+    return UV_ENOMEM;
+  loop->internal_fields = lfields;
+
+  err = uv_mutex_init(&lfields->loop_metrics.lock);
+  if (err)
+    goto fail_metrics_mutex_init;
+
+  /* To prevent uninitialized memory access, loop->time must be initialized
+   * to zero before calling uv_update_time for the first time.
+   */
+  loop->time = 0;
+  uv_update_time(loop);
+
+  QUEUE_INIT(&loop->wq);
+  QUEUE_INIT(&loop->handle_queue);
+  loop->active_reqs.count = 0;
+  loop->active_handles = 0;
+
+  loop->pending_reqs_tail = NULL;
+
+  loop->endgame_handles = NULL;
+
+  loop->timer_heap = timer_heap = (heap*)uv__malloc(sizeof(*timer_heap));
+  if (timer_heap == NULL) {
+    err = UV_ENOMEM;
+    goto fail_timers_alloc;
+  }
+
+  heap_init(timer_heap);
+
+  loop->check_handles = NULL;
+  loop->prepare_handles = NULL;
+  loop->idle_handles = NULL;
+
+  loop->next_prepare_handle = NULL;
+  loop->next_check_handle = NULL;
+  loop->next_idle_handle = NULL;
+
+  memset(&loop->poll_peer_sockets, 0, sizeof loop->poll_peer_sockets);
+
+  loop->active_tcp_streams = 0;
+  loop->active_udp_streams = 0;
+
+  loop->timer_counter = 0;
+  loop->stop_flag = 0;
+
+  err = uv_mutex_init(&loop->wq_mutex);
+  if (err)
+    goto fail_mutex_init;
+
+  err = uv_async_init(loop, &loop->wq_async, uv__work_done);
+  if (err)
+    goto fail_async_init;
+
+  uv__handle_unref(&loop->wq_async);
+  loop->wq_async.flags |= UV_HANDLE_INTERNAL;
+
+  err = uv__loops_add(loop);
+  if (err)
+    goto fail_async_init;
+
+  return 0;
+
+fail_async_init:
+  uv_mutex_destroy(&loop->wq_mutex);
+
+fail_mutex_init:
+  uv__free(timer_heap);
+  loop->timer_heap = NULL;
+
+fail_timers_alloc:
+  uv_mutex_destroy(&lfields->loop_metrics.lock);
+
+fail_metrics_mutex_init:
+  uv__free(lfields);
+  loop->internal_fields = NULL;
+  CloseHandle(loop->iocp);
+  loop->iocp = INVALID_HANDLE_VALUE;
+
+  return err;
+}
+
+
+void uv_update_time(uv_loop_t* loop) {
+  uint64_t new_time = uv__hrtime(1000);
+  assert(new_time >= loop->time);
+  loop->time = new_time;
+}
+
+
+void uv__once_init(void) {
+  uv_once(&uv_init_guard_, uv__init);
+}
+
+
+void uv__loop_close(uv_loop_t* loop) {
+  uv__loop_internal_fields_t* lfields;
+  size_t i;
+
+  uv__loops_remove(loop);
+
+  /* Close the async handle without needing an extra loop iteration.
+   * We might have a pending message, but we're just going to destroy the IOCP
+   * soon, so we can just discard it now without the usual risk of a getting
+   * another notification from GetQueuedCompletionStatusEx after calling the
+   * close_cb (which we also skip defining). We'll assert later that queue was
+   * actually empty and all reqs handled. */
+  loop->wq_async.async_sent = 0;
+  loop->wq_async.close_cb = NULL;
+  uv__handle_closing(&loop->wq_async);
+  uv__handle_close(&loop->wq_async);
+
+  for (i = 0; i < ARRAY_SIZE(loop->poll_peer_sockets); i++) {
+    SOCKET sock = loop->poll_peer_sockets[i];
+    if (sock != 0 && sock != INVALID_SOCKET)
+      closesocket(sock);
+  }
+
+  uv_mutex_lock(&loop->wq_mutex);
+  assert(QUEUE_EMPTY(&loop->wq) && "thread pool work queue not empty!");
+  assert(!uv__has_active_reqs(loop));
+  uv_mutex_unlock(&loop->wq_mutex);
+  uv_mutex_destroy(&loop->wq_mutex);
+
+  uv__free(loop->timer_heap);
+  loop->timer_heap = NULL;
+
+  lfields = uv__get_internal_fields(loop);
+  uv_mutex_destroy(&lfields->loop_metrics.lock);
+  uv__free(lfields);
+  loop->internal_fields = NULL;
+
+  CloseHandle(loop->iocp);
+}
+
+
+int uv__loop_configure(uv_loop_t* loop, uv_loop_option option, va_list ap) {
+  uv__loop_internal_fields_t* lfields;
+
+  lfields = uv__get_internal_fields(loop);
+  if (option == UV_METRICS_IDLE_TIME) {
+    lfields->flags |= UV_METRICS_IDLE_TIME;
+    return 0;
+  }
+
+  return UV_ENOSYS;
+}
+
+
+int uv_backend_fd(const uv_loop_t* loop) {
+  return -1;
+}
+
+
+int uv_loop_fork(uv_loop_t* loop) {
+  return UV_ENOSYS;
+}
+
+
+static int uv__loop_alive(const uv_loop_t* loop) {
+  return uv__has_active_handles(loop) ||
+         uv__has_active_reqs(loop) ||
+         loop->pending_reqs_tail != NULL ||
+         loop->endgame_handles != NULL;
+}
+
+
+int uv_loop_alive(const uv_loop_t* loop) {
+  return uv__loop_alive(loop);
+}
+
+
+int uv_backend_timeout(const uv_loop_t* loop) {
+  if (loop->stop_flag == 0 &&
+      /* uv__loop_alive(loop) && */
+      (uv__has_active_handles(loop) || uv__has_active_reqs(loop)) &&
+      loop->pending_reqs_tail == NULL &&
+      loop->idle_handles == NULL &&
+      loop->endgame_handles == NULL)
+    return uv__next_timeout(loop);
+  return 0;
+}
+
+
+static void uv__poll_wine(uv_loop_t* loop, DWORD timeout) {
+  DWORD bytes;
+  ULONG_PTR key;
+  OVERLAPPED* overlapped;
+  uv_req_t* req;
+  int repeat;
+  uint64_t timeout_time;
+  uint64_t user_timeout;
+  int reset_timeout;
+
+  timeout_time = loop->time + timeout;
+
+  if (uv__get_internal_fields(loop)->flags & UV_METRICS_IDLE_TIME) {
+    reset_timeout = 1;
+    user_timeout = timeout;
+    timeout = 0;
+  } else {
+    reset_timeout = 0;
+  }
+
+  for (repeat = 0; ; repeat++) {
+    /* Only need to set the provider_entry_time if timeout != 0. The function
+     * will return early if the loop isn't configured with UV_METRICS_IDLE_TIME.
+     */
+    if (timeout != 0)
+      uv__metrics_set_provider_entry_time(loop);
+
+    GetQueuedCompletionStatus(loop->iocp,
+                              &bytes,
+                              &key,
+                              &overlapped,
+                              timeout);
+
+    if (reset_timeout != 0) {
+      timeout = user_timeout;
+      reset_timeout = 0;
+    }
+
+    /* Placed here because on success the loop will break whether there is an
+     * empty package or not, or if GetQueuedCompletionStatus returned early then
+     * the timeout will be updated and the loop will run again. In either case
+     * the idle time will need to be updated.
+     */
+    uv__metrics_update_idle_time(loop);
+
+    if (overlapped) {
+      /* Package was dequeued */
+      req = uv__overlapped_to_req(overlapped);
+      uv__insert_pending_req(loop, req);
+
+      /* Some time might have passed waiting for I/O,
+       * so update the loop time here.
+       */
+      uv_update_time(loop);
+    } else if (GetLastError() != WAIT_TIMEOUT) {
+      /* Serious error */
+      uv_fatal_error(GetLastError(), "GetQueuedCompletionStatus");
+    } else if (timeout > 0) {
+      /* GetQueuedCompletionStatus can occasionally return a little early.
+       * Make sure that the desired timeout target time is reached.
+       */
+      uv_update_time(loop);
+      if (timeout_time > loop->time) {
+        timeout = (DWORD)(timeout_time - loop->time);
+        /* The first call to GetQueuedCompletionStatus should return very
+         * close to the target time and the second should reach it, but
+         * this is not stated in the documentation. To make sure a busy
+         * loop cannot happen, the timeout is increased exponentially
+         * starting on the third round.
+         */
+        timeout += repeat ? (1 << (repeat - 1)) : 0;
+        continue;
+      }
+    }
+    break;
+  }
+}
+
+
+static void uv__poll(uv_loop_t* loop, DWORD timeout) {
+  BOOL success;
+  uv_req_t* req;
+  OVERLAPPED_ENTRY overlappeds[128];
+  ULONG count;
+  ULONG i;
+  int repeat;
+  uint64_t timeout_time;
+  uint64_t user_timeout;
+  int reset_timeout;
+
+  timeout_time = loop->time + timeout;
+
+  if (uv__get_internal_fields(loop)->flags & UV_METRICS_IDLE_TIME) {
+    reset_timeout = 1;
+    user_timeout = timeout;
+    timeout = 0;
+  } else {
+    reset_timeout = 0;
+  }
+
+  for (repeat = 0; ; repeat++) {
+    /* Only need to set the provider_entry_time if timeout != 0. The function
+     * will return early if the loop isn't configured with UV_METRICS_IDLE_TIME.
+     */
+    if (timeout != 0)
+      uv__metrics_set_provider_entry_time(loop);
+
+    success = pGetQueuedCompletionStatusEx(loop->iocp,
+                                           overlappeds,
+                                           ARRAY_SIZE(overlappeds),
+                                           &count,
+                                           timeout,
+                                           FALSE);
+
+    if (reset_timeout != 0) {
+      timeout = user_timeout;
+      reset_timeout = 0;
+    }
+
+    /* Placed here because on success the loop will break whether there is an
+     * empty package or not, or if GetQueuedCompletionStatus returned early then
+     * the timeout will be updated and the loop will run again. In either case
+     * the idle time will need to be updated.
+     */
+    uv__metrics_update_idle_time(loop);
+
+    if (success) {
+      for (i = 0; i < count; i++) {
+        /* Package was dequeued, but see if it is not a empty package
+         * meant only to wake us up.
+         */
+        if (overlappeds[i].lpOverlapped) {
+          req = uv__overlapped_to_req(overlappeds[i].lpOverlapped);
+          uv__insert_pending_req(loop, req);
+        }
+      }
+
+      /* Some time might have passed waiting for I/O,
+       * so update the loop time here.
+       */
+      uv_update_time(loop);
+    } else if (GetLastError() != WAIT_TIMEOUT) {
+      /* Serious error */
+      uv_fatal_error(GetLastError(), "GetQueuedCompletionStatusEx");
+    } else if (timeout > 0) {
+      /* GetQueuedCompletionStatus can occasionally return a little early.
+       * Make sure that the desired timeout target time is reached.
+       */
+      uv_update_time(loop);
+      if (timeout_time > loop->time) {
+        timeout = (DWORD)(timeout_time - loop->time);
+        /* The first call to GetQueuedCompletionStatus should return very
+         * close to the target time and the second should reach it, but
+         * this is not stated in the documentation. To make sure a busy
+         * loop cannot happen, the timeout is increased exponentially
+         * starting on the third round.
+         */
+        timeout += repeat ? (1 << (repeat - 1)) : 0;
+        continue;
+      }
+    }
+    break;
+  }
+}
+
+
+int uv_run(uv_loop_t *loop, uv_run_mode mode) {
+  DWORD timeout;
+  int r;
+  int can_sleep;
+
+  r = uv__loop_alive(loop);
+  if (!r)
+    uv_update_time(loop);
+
+  while (r != 0 && loop->stop_flag == 0) {
+    uv_update_time(loop);
+    uv__run_timers(loop);
+
+    can_sleep = loop->pending_reqs_tail == NULL && loop->idle_handles == NULL;
+
+    uv__process_reqs(loop);
+    uv__idle_invoke(loop);
+    uv__prepare_invoke(loop);
+
+    timeout = 0;
+    if ((mode == UV_RUN_ONCE && can_sleep) || mode == UV_RUN_DEFAULT)
+      timeout = uv_backend_timeout(loop);
+
+    if (pGetQueuedCompletionStatusEx)
+      uv__poll(loop, timeout);
+    else
+      uv__poll_wine(loop, timeout);
+
+    /* Process immediate callbacks (e.g. write_cb) a small fixed number of
+     * times to avoid loop starvation.*/
+    for (r = 0; r < 8 && loop->pending_reqs_tail != NULL; r++)
+      uv__process_reqs(loop);
+
+    /* Run one final update on the provider_idle_time in case uv__poll*
+     * returned because the timeout expired, but no events were received. This
+     * call will be ignored if the provider_entry_time was either never set (if
+     * the timeout == 0) or was already updated b/c an event was received.
+     */
+    uv__metrics_update_idle_time(loop);
+
+    uv__check_invoke(loop);
+    uv__process_endgames(loop);
+
+    if (mode == UV_RUN_ONCE) {
+      /* UV_RUN_ONCE implies forward progress: at least one callback must have
+       * been invoked when it returns. uv__io_poll() can return without doing
+       * I/O (meaning: no callbacks) when its timeout expires - which means we
+       * have pending timers that satisfy the forward progress constraint.
+       *
+       * UV_RUN_NOWAIT makes no guarantees about progress so it's omitted from
+       * the check.
+       */
+      uv_update_time(loop);
+      uv__run_timers(loop);
+    }
+
+    r = uv__loop_alive(loop);
+    if (mode == UV_RUN_ONCE || mode == UV_RUN_NOWAIT)
+      break;
+  }
+
+  /* The if statement lets the compiler compile it to a conditional store.
+   * Avoids dirtying a cache line.
+   */
+  if (loop->stop_flag != 0)
+    loop->stop_flag = 0;
+
+  return r;
+}
+
+
+int uv_fileno(const uv_handle_t* handle, uv_os_fd_t* fd) {
+  uv_os_fd_t fd_out;
+
+  switch (handle->type) {
+  case UV_TCP:
+    fd_out = (uv_os_fd_t)((uv_tcp_t*) handle)->socket;
+    break;
+
+  case UV_NAMED_PIPE:
+    fd_out = ((uv_pipe_t*) handle)->handle;
+    break;
+
+  case UV_TTY:
+    fd_out = ((uv_tty_t*) handle)->handle;
+    break;
+
+  case UV_UDP:
+    fd_out = (uv_os_fd_t)((uv_udp_t*) handle)->socket;
+    break;
+
+  case UV_POLL:
+    fd_out = (uv_os_fd_t)((uv_poll_t*) handle)->socket;
+    break;
+
+  default:
+    return UV_EINVAL;
+  }
+
+  if (uv_is_closing(handle) || fd_out == INVALID_HANDLE_VALUE)
+    return UV_EBADF;
+
+  *fd = fd_out;
+  return 0;
+}
+
+
+int uv__socket_sockopt(uv_handle_t* handle, int optname, int* value) {
+  int r;
+  int len;
+  SOCKET socket;
+
+  if (handle == NULL || value == NULL)
+    return UV_EINVAL;
+
+  if (handle->type == UV_TCP)
+    socket = ((uv_tcp_t*) handle)->socket;
+  else if (handle->type == UV_UDP)
+    socket = ((uv_udp_t*) handle)->socket;
+  else
+    return UV_ENOTSUP;
+
+  len = sizeof(*value);
+
+  if (*value == 0)
+    r = getsockopt(socket, SOL_SOCKET, optname, (char*) value, &len);
+  else
+    r = setsockopt(socket, SOL_SOCKET, optname, (const char*) value, len);
+
+  if (r == SOCKET_ERROR)
+    return uv_translate_sys_error(WSAGetLastError());
+
+  return 0;
+}
+
+int uv_cpumask_size(void) {
+  return (int)(sizeof(DWORD_PTR) * 8);
+}
+
+int uv__getsockpeername(const uv_handle_t* handle,
+                        uv__peersockfunc func,
+                        struct sockaddr* name,
+                        int* namelen,
+                        int delayed_error) {
+
+  int result;
+  uv_os_fd_t fd;
+
+  result = uv_fileno(handle, &fd);
+  if (result != 0)
+    return result;
+
+  if (delayed_error)
+    return uv_translate_sys_error(delayed_error);
+
+  result = func((SOCKET) fd, name, namelen);
+  if (result != 0)
+    return uv_translate_sys_error(WSAGetLastError());
+
+  return 0;
+}
diff --git a/wpinet/src/main/native/thirdparty/libuv/src/win/detect-wakeup.cpp b/wpinet/src/main/native/thirdparty/libuv/src/win/detect-wakeup.cpp
new file mode 100644
index 0000000..ab19361
--- /dev/null
+++ b/wpinet/src/main/native/thirdparty/libuv/src/win/detect-wakeup.cpp
@@ -0,0 +1,56 @@
+/* Copyright libuv project contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include "uv.h"
+#include "internal.h"
+#include "winapi.h"
+
+static void uv__register_system_resume_callback(void);
+
+void uv__init_detect_system_wakeup(void) {
+  /* Try registering system power event callback. This is the cleanest
+   * method, but it will only work on Win8 and above.
+   */
+  uv__register_system_resume_callback();
+}
+
+static ULONG CALLBACK uv__system_resume_callback(PVOID Context,
+                                                 ULONG Type,
+                                                 PVOID Setting) {
+  if (Type == PBT_APMRESUMESUSPEND || Type == PBT_APMRESUMEAUTOMATIC)
+    uv__wake_all_loops();
+
+  return 0;
+}
+
+static void uv__register_system_resume_callback(void) {
+  _DEVICE_NOTIFY_SUBSCRIBE_PARAMETERS recipient;
+  _HPOWERNOTIFY registration_handle;
+
+  if (pPowerRegisterSuspendResumeNotification == NULL)
+    return;
+
+  recipient.Callback = uv__system_resume_callback;
+  recipient.Context = NULL;
+  (*pPowerRegisterSuspendResumeNotification)(DEVICE_NOTIFY_CALLBACK,
+                                             &recipient,
+                                             &registration_handle);
+}
diff --git a/wpinet/src/main/native/thirdparty/libuv/src/win/dl.cpp b/wpinet/src/main/native/thirdparty/libuv/src/win/dl.cpp
new file mode 100644
index 0000000..676be4d
--- /dev/null
+++ b/wpinet/src/main/native/thirdparty/libuv/src/win/dl.cpp
@@ -0,0 +1,136 @@
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include "uv.h"
+#include "internal.h"
+
+static int uv__dlerror(uv_lib_t* lib, const char* filename, DWORD errorno);
+
+
+int uv_dlopen(const char* filename, uv_lib_t* lib) {
+  WCHAR filename_w[32768];
+
+  lib->handle = NULL;
+  lib->errmsg = NULL;
+
+  if (!MultiByteToWideChar(CP_UTF8,
+                           0,
+                           filename,
+                           -1,
+                           filename_w,
+                           ARRAY_SIZE(filename_w))) {
+    return uv__dlerror(lib, filename, GetLastError());
+  }
+
+  lib->handle = LoadLibraryExW(filename_w, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
+  if (lib->handle == NULL) {
+    return uv__dlerror(lib, filename, GetLastError());
+  }
+
+  return 0;
+}
+
+
+void uv_dlclose(uv_lib_t* lib) {
+  if (lib->errmsg) {
+    LocalFree((void*)lib->errmsg);
+    lib->errmsg = NULL;
+  }
+
+  if (lib->handle) {
+    /* Ignore errors. No good way to signal them without leaking memory. */
+    FreeLibrary(lib->handle);
+    lib->handle = NULL;
+  }
+}
+
+
+int uv_dlsym(uv_lib_t* lib, const char* name, void** ptr) {
+  /* Cast though integer to suppress pedantic warning about forbidden cast. */
+  *ptr = (void*)(uintptr_t) GetProcAddress(lib->handle, name);
+  return uv__dlerror(lib, "", *ptr ? 0 : GetLastError());
+}
+
+
+const char* uv_dlerror(const uv_lib_t* lib) {
+  return lib->errmsg ? lib->errmsg : "no error";
+}
+
+
+static void uv__format_fallback_error(uv_lib_t* lib, int errorno){
+  static const CHAR fallback_error[] = "error: %1!d!";
+  DWORD_PTR args[1];
+  args[0] = (DWORD_PTR) errorno;
+
+  FormatMessageA(FORMAT_MESSAGE_FROM_STRING |
+                 FORMAT_MESSAGE_ARGUMENT_ARRAY |
+                 FORMAT_MESSAGE_ALLOCATE_BUFFER,
+                 fallback_error, 0, 0,
+                 (LPSTR) &lib->errmsg,
+                 0, (va_list*) args);
+}
+
+
+
+static int uv__dlerror(uv_lib_t* lib, const char* filename, DWORD errorno) {
+  DWORD_PTR arg;
+  DWORD res;
+  char* msg;
+
+  if (lib->errmsg) {
+    LocalFree(lib->errmsg);
+    lib->errmsg = NULL;
+  }
+
+  if (errorno == 0)
+    return 0;
+
+  res = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER |
+                       FORMAT_MESSAGE_FROM_SYSTEM |
+                       FORMAT_MESSAGE_IGNORE_INSERTS, NULL, errorno,
+                       MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US),
+                       (LPSTR) &lib->errmsg, 0, NULL);
+
+  if (!res && (GetLastError() == ERROR_MUI_FILE_NOT_FOUND ||
+               GetLastError() == ERROR_RESOURCE_TYPE_NOT_FOUND)) {
+    res = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER |
+                         FORMAT_MESSAGE_FROM_SYSTEM |
+                         FORMAT_MESSAGE_IGNORE_INSERTS, NULL, errorno,
+                         0, (LPSTR) &lib->errmsg, 0, NULL);
+  }
+
+  if (res && errorno == ERROR_BAD_EXE_FORMAT && strstr(lib->errmsg, "%1")) {
+    msg = lib->errmsg;
+    lib->errmsg = NULL;
+    arg = (DWORD_PTR) filename;
+    res = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER |
+                         FORMAT_MESSAGE_ARGUMENT_ARRAY |
+                         FORMAT_MESSAGE_FROM_STRING,
+                         msg,
+                         0, 0, (LPSTR) &lib->errmsg, 0, (va_list*) &arg);
+    LocalFree(msg);
+  }
+
+  if (!res)
+    uv__format_fallback_error(lib, errorno);
+
+  return -1;
+}
diff --git a/wpinet/src/main/native/thirdparty/libuv/src/win/error.cpp b/wpinet/src/main/native/thirdparty/libuv/src/win/error.cpp
new file mode 100644
index 0000000..3a269da
--- /dev/null
+++ b/wpinet/src/main/native/thirdparty/libuv/src/win/error.cpp
@@ -0,0 +1,173 @@
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include <assert.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "uv.h"
+#include "internal.h"
+
+
+/*
+ * Display an error message and abort the event loop.
+ */
+void uv_fatal_error(const int errorno, const char* syscall) {
+  char* buf = NULL;
+  const char* errmsg;
+
+  FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
+      FORMAT_MESSAGE_IGNORE_INSERTS, NULL, errorno,
+      MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&buf, 0, NULL);
+
+  if (buf) {
+    errmsg = buf;
+  } else {
+    errmsg = "Unknown error";
+  }
+
+  /* FormatMessage messages include a newline character already, so don't add
+   * another. */
+  if (syscall) {
+    fprintf(stderr, "%s: (%d) %s", syscall, errorno, errmsg);
+  } else {
+    fprintf(stderr, "(%d) %s", errorno, errmsg);
+  }
+
+  if (buf) {
+    LocalFree(buf);
+  }
+
+  DebugBreak();
+  abort();
+}
+
+
+int uv_translate_sys_error(int sys_errno) {
+  if (sys_errno <= 0) {
+    return sys_errno;  /* If < 0 then it's already a libuv error. */
+  }
+
+  switch (sys_errno) {
+    case ERROR_NOACCESS:                    return UV_EACCES;
+    case WSAEACCES:                         return UV_EACCES;
+    case ERROR_ELEVATION_REQUIRED:          return UV_EACCES;
+    case ERROR_CANT_ACCESS_FILE:            return UV_EACCES;
+    case ERROR_ADDRESS_ALREADY_ASSOCIATED:  return UV_EADDRINUSE;
+    case WSAEADDRINUSE:                     return UV_EADDRINUSE;
+    case WSAEADDRNOTAVAIL:                  return UV_EADDRNOTAVAIL;
+    case WSAEAFNOSUPPORT:                   return UV_EAFNOSUPPORT;
+    case WSAEWOULDBLOCK:                    return UV_EAGAIN;
+    case WSAEALREADY:                       return UV_EALREADY;
+    case ERROR_INVALID_FLAGS:               return UV_EBADF;
+    case ERROR_INVALID_HANDLE:              return UV_EBADF;
+    case ERROR_LOCK_VIOLATION:              return UV_EBUSY;
+    case ERROR_PIPE_BUSY:                   return UV_EBUSY;
+    case ERROR_SHARING_VIOLATION:           return UV_EBUSY;
+    case ERROR_OPERATION_ABORTED:           return UV_ECANCELED;
+    case WSAEINTR:                          return UV_ECANCELED;
+    case ERROR_NO_UNICODE_TRANSLATION:      return UV_ECHARSET;
+    case ERROR_CONNECTION_ABORTED:          return UV_ECONNABORTED;
+    case WSAECONNABORTED:                   return UV_ECONNABORTED;
+    case ERROR_CONNECTION_REFUSED:          return UV_ECONNREFUSED;
+    case WSAECONNREFUSED:                   return UV_ECONNREFUSED;
+    case ERROR_NETNAME_DELETED:             return UV_ECONNRESET;
+    case WSAECONNRESET:                     return UV_ECONNRESET;
+    case ERROR_ALREADY_EXISTS:              return UV_EEXIST;
+    case ERROR_FILE_EXISTS:                 return UV_EEXIST;
+    case ERROR_BUFFER_OVERFLOW:             return UV_EFAULT;
+    case WSAEFAULT:                         return UV_EFAULT;
+    case ERROR_HOST_UNREACHABLE:            return UV_EHOSTUNREACH;
+    case WSAEHOSTUNREACH:                   return UV_EHOSTUNREACH;
+    case ERROR_INSUFFICIENT_BUFFER:         return UV_EINVAL;
+    case ERROR_INVALID_DATA:                return UV_EINVAL;
+    case ERROR_INVALID_PARAMETER:           return UV_EINVAL;
+    case ERROR_SYMLINK_NOT_SUPPORTED:       return UV_EINVAL;
+    case WSAEINVAL:                         return UV_EINVAL;
+    case WSAEPFNOSUPPORT:                   return UV_EINVAL;
+    case ERROR_BEGINNING_OF_MEDIA:          return UV_EIO;
+    case ERROR_BUS_RESET:                   return UV_EIO;
+    case ERROR_CRC:                         return UV_EIO;
+    case ERROR_DEVICE_DOOR_OPEN:            return UV_EIO;
+    case ERROR_DEVICE_REQUIRES_CLEANING:    return UV_EIO;
+    case ERROR_DISK_CORRUPT:                return UV_EIO;
+    case ERROR_EOM_OVERFLOW:                return UV_EIO;
+    case ERROR_FILEMARK_DETECTED:           return UV_EIO;
+    case ERROR_GEN_FAILURE:                 return UV_EIO;
+    case ERROR_INVALID_BLOCK_LENGTH:        return UV_EIO;
+    case ERROR_IO_DEVICE:                   return UV_EIO;
+    case ERROR_NO_DATA_DETECTED:            return UV_EIO;
+    case ERROR_NO_SIGNAL_SENT:              return UV_EIO;
+    case ERROR_OPEN_FAILED:                 return UV_EIO;
+    case ERROR_SETMARK_DETECTED:            return UV_EIO;
+    case ERROR_SIGNAL_REFUSED:              return UV_EIO;
+    case WSAEISCONN:                        return UV_EISCONN;
+    case ERROR_CANT_RESOLVE_FILENAME:       return UV_ELOOP;
+    case ERROR_TOO_MANY_OPEN_FILES:         return UV_EMFILE;
+    case WSAEMFILE:                         return UV_EMFILE;
+    case WSAEMSGSIZE:                       return UV_EMSGSIZE;
+    case ERROR_FILENAME_EXCED_RANGE:        return UV_ENAMETOOLONG;
+    case ERROR_NETWORK_UNREACHABLE:         return UV_ENETUNREACH;
+    case WSAENETUNREACH:                    return UV_ENETUNREACH;
+    case WSAENOBUFS:                        return UV_ENOBUFS;
+    case ERROR_BAD_PATHNAME:                return UV_ENOENT;
+    case ERROR_DIRECTORY:                   return UV_ENOENT;
+    case ERROR_ENVVAR_NOT_FOUND:            return UV_ENOENT;
+    case ERROR_FILE_NOT_FOUND:              return UV_ENOENT;
+    case ERROR_INVALID_NAME:                return UV_ENOENT;
+    case ERROR_INVALID_DRIVE:               return UV_ENOENT;
+    case ERROR_INVALID_REPARSE_DATA:        return UV_ENOENT;
+    case ERROR_MOD_NOT_FOUND:               return UV_ENOENT;
+    case ERROR_PATH_NOT_FOUND:              return UV_ENOENT;
+    case WSAHOST_NOT_FOUND:                 return UV_ENOENT;
+    case WSANO_DATA:                        return UV_ENOENT;
+    case ERROR_NOT_ENOUGH_MEMORY:           return UV_ENOMEM;
+    case ERROR_OUTOFMEMORY:                 return UV_ENOMEM;
+    case ERROR_CANNOT_MAKE:                 return UV_ENOSPC;
+    case ERROR_DISK_FULL:                   return UV_ENOSPC;
+    case ERROR_EA_TABLE_FULL:               return UV_ENOSPC;
+    case ERROR_END_OF_MEDIA:                return UV_ENOSPC;
+    case ERROR_HANDLE_DISK_FULL:            return UV_ENOSPC;
+    case ERROR_NOT_CONNECTED:               return UV_ENOTCONN;
+    case WSAENOTCONN:                       return UV_ENOTCONN;
+    case ERROR_DIR_NOT_EMPTY:               return UV_ENOTEMPTY;
+    case WSAENOTSOCK:                       return UV_ENOTSOCK;
+    case ERROR_NOT_SUPPORTED:               return UV_ENOTSUP;
+    case ERROR_BROKEN_PIPE:                 return UV_EOF;
+    case ERROR_ACCESS_DENIED:               return UV_EPERM;
+    case ERROR_PRIVILEGE_NOT_HELD:          return UV_EPERM;
+    case ERROR_BAD_PIPE:                    return UV_EPIPE;
+    case ERROR_NO_DATA:                     return UV_EPIPE;
+    case ERROR_PIPE_NOT_CONNECTED:          return UV_EPIPE;
+    case WSAESHUTDOWN:                      return UV_EPIPE;
+    case WSAEPROTONOSUPPORT:                return UV_EPROTONOSUPPORT;
+    case ERROR_WRITE_PROTECT:               return UV_EROFS;
+    case ERROR_SEM_TIMEOUT:                 return UV_ETIMEDOUT;
+    case WSAETIMEDOUT:                      return UV_ETIMEDOUT;
+    case ERROR_NOT_SAME_DEVICE:             return UV_EXDEV;
+    case ERROR_INVALID_FUNCTION:            return UV_EISDIR;
+    case ERROR_META_EXPANSION_TOO_LONG:     return UV_E2BIG;
+    case WSAESOCKTNOSUPPORT:                return UV_ESOCKTNOSUPPORT;
+    default:                                return UV_UNKNOWN;
+  }
+}
diff --git a/wpinet/src/main/native/thirdparty/libuv/src/win/fs-event.cpp b/wpinet/src/main/native/thirdparty/libuv/src/win/fs-event.cpp
new file mode 100644
index 0000000..3244a4e
--- /dev/null
+++ b/wpinet/src/main/native/thirdparty/libuv/src/win/fs-event.cpp
@@ -0,0 +1,610 @@
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#define _CRT_NONSTDC_NO_WARNINGS
+
+#include <assert.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "uv.h"
+#include "internal.h"
+#include "handle-inl.h"
+#include "req-inl.h"
+
+
+const unsigned int uv_directory_watcher_buffer_size = 4096;
+
+
+static void uv__fs_event_queue_readdirchanges(uv_loop_t* loop,
+    uv_fs_event_t* handle) {
+  assert(handle->dir_handle != INVALID_HANDLE_VALUE);
+  assert(!handle->req_pending);
+
+  memset(&(handle->req.u.io.overlapped), 0,
+         sizeof(handle->req.u.io.overlapped));
+  if (!ReadDirectoryChangesW(handle->dir_handle,
+                             handle->buffer,
+                             uv_directory_watcher_buffer_size,
+                             (handle->flags & UV_FS_EVENT_RECURSIVE) ? TRUE : FALSE,
+                             FILE_NOTIFY_CHANGE_FILE_NAME      |
+                               FILE_NOTIFY_CHANGE_DIR_NAME     |
+                               FILE_NOTIFY_CHANGE_ATTRIBUTES   |
+                               FILE_NOTIFY_CHANGE_SIZE         |
+                               FILE_NOTIFY_CHANGE_LAST_WRITE   |
+                               FILE_NOTIFY_CHANGE_LAST_ACCESS  |
+                               FILE_NOTIFY_CHANGE_CREATION     |
+                               FILE_NOTIFY_CHANGE_SECURITY,
+                             NULL,
+                             &handle->req.u.io.overlapped,
+                             NULL)) {
+    /* Make this req pending reporting an error. */
+    SET_REQ_ERROR(&handle->req, GetLastError());
+    uv__insert_pending_req(loop, (uv_req_t*)&handle->req);
+  }
+
+  handle->req_pending = 1;
+}
+
+static void uv__relative_path(const WCHAR* filename,
+                              const WCHAR* dir,
+                              WCHAR** relpath) {
+  size_t relpathlen;
+  size_t filenamelen = wcslen(filename);
+  size_t dirlen = wcslen(dir);
+  assert(!_wcsnicmp(filename, dir, dirlen));
+  if (dirlen > 0 && dir[dirlen - 1] == '\\')
+    dirlen--;
+  relpathlen = filenamelen - dirlen - 1;
+  *relpath = (WCHAR*)uv__malloc((relpathlen + 1) * sizeof(WCHAR));
+  if (!*relpath)
+    uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc");
+  wcsncpy(*relpath, filename + dirlen + 1, relpathlen);
+  (*relpath)[relpathlen] = L'\0';
+}
+
+static int uv__split_path(const WCHAR* filename, WCHAR** dir,
+    WCHAR** file) {
+  size_t len, i;
+  DWORD dir_len;
+
+  if (filename == NULL) {
+    if (dir != NULL)
+      *dir = NULL;
+    *file = NULL;
+    return 0;
+  }
+
+  len = wcslen(filename);
+  i = len;
+  while (i > 0 && filename[--i] != '\\' && filename[i] != '/');
+
+  if (i == 0) {
+    if (dir) {
+      dir_len = GetCurrentDirectoryW(0, NULL);
+      if (dir_len == 0) {
+        return -1;
+      }
+      *dir = (WCHAR*)uv__malloc(dir_len * sizeof(WCHAR));
+      if (!*dir) {
+        uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc");
+      }
+
+      if (!GetCurrentDirectoryW(dir_len, *dir)) {
+        uv__free(*dir);
+        *dir = NULL;
+        return -1;
+      }
+    }
+
+    *file = wcsdup(filename);
+  } else {
+    if (dir) {
+      *dir = (WCHAR*)uv__malloc((i + 2) * sizeof(WCHAR));
+      if (!*dir) {
+        uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc");
+      }
+      wcsncpy(*dir, filename, i + 1);
+      (*dir)[i + 1] = L'\0';
+    }
+
+    *file = (WCHAR*)uv__malloc((len - i) * sizeof(WCHAR));
+    if (!*file) {
+      uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc");
+    }
+    wcsncpy(*file, filename + i + 1, len - i - 1);
+    (*file)[len - i - 1] = L'\0';
+  }
+
+  return 0;
+}
+
+
+int uv_fs_event_init(uv_loop_t* loop, uv_fs_event_t* handle) {
+  uv__handle_init(loop, (uv_handle_t*) handle, UV_FS_EVENT);
+  handle->dir_handle = INVALID_HANDLE_VALUE;
+  handle->buffer = NULL;
+  handle->req_pending = 0;
+  handle->filew = NULL;
+  handle->short_filew = NULL;
+  handle->dirw = NULL;
+
+  UV_REQ_INIT(&handle->req, UV_FS_EVENT_REQ);
+  handle->req.data = handle;
+
+  return 0;
+}
+
+
+int uv_fs_event_start(uv_fs_event_t* handle,
+                      uv_fs_event_cb cb,
+                      const char* path,
+                      unsigned int flags) {
+  int name_size, is_path_dir, size;
+  DWORD attr, last_error;
+  WCHAR* dir = NULL, *dir_to_watch, *pathw = NULL;
+  DWORD short_path_buffer_len;
+  WCHAR *short_path_buffer;
+  WCHAR* short_path, *long_path;
+
+  short_path = NULL;
+  if (uv__is_active(handle))
+    return UV_EINVAL;
+
+  handle->cb = cb;
+  handle->path = uv__strdup(path);
+  if (!handle->path) {
+    uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc");
+  }
+
+  uv__handle_start(handle);
+
+  /* Convert name to UTF16. */
+
+  name_size = MultiByteToWideChar(CP_UTF8, 0, path, -1, NULL, 0) *
+              sizeof(WCHAR);
+  pathw = (WCHAR*)uv__malloc(name_size);
+  if (!pathw) {
+    uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc");
+  }
+
+  if (!MultiByteToWideChar(CP_UTF8,
+                           0,
+                           path,
+                           -1,
+                           pathw,
+                           name_size / sizeof(WCHAR))) {
+    return uv_translate_sys_error(GetLastError());
+  }
+
+  /* Determine whether path is a file or a directory. */
+  attr = GetFileAttributesW(pathw);
+  if (attr == INVALID_FILE_ATTRIBUTES) {
+    last_error = GetLastError();
+    goto error;
+  }
+
+  is_path_dir = (attr & FILE_ATTRIBUTE_DIRECTORY) ? 1 : 0;
+
+  if (is_path_dir) {
+     /* path is a directory, so that's the directory that we will watch. */
+
+    /* Convert to long path. */
+    size = GetLongPathNameW(pathw, NULL, 0);
+
+    if (size) {
+      long_path = (WCHAR*)uv__malloc(size * sizeof(WCHAR));
+      if (!long_path) {
+        uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc");
+      }
+
+      size = GetLongPathNameW(pathw, long_path, size);
+      if (size) {
+        long_path[size] = '\0';
+      } else {
+        uv__free(long_path);
+        long_path = NULL;
+      }
+
+      if (long_path) {
+        uv__free(pathw);
+        pathw = long_path;
+      }
+    }
+
+    dir_to_watch = pathw;
+  } else {
+    /*
+     * path is a file.  So we split path into dir & file parts, and
+     * watch the dir directory.
+     */
+
+    /* Convert to short path. */
+    short_path_buffer = NULL;
+    short_path_buffer_len = GetShortPathNameW(pathw, NULL, 0);
+    if (short_path_buffer_len == 0) {
+      goto short_path_done;
+    }
+    short_path_buffer = (WCHAR*)uv__malloc(short_path_buffer_len * sizeof(WCHAR));
+    if (short_path_buffer == NULL) {
+      goto short_path_done;
+    }
+    if (GetShortPathNameW(pathw,
+                          short_path_buffer,
+                          short_path_buffer_len) == 0) {
+      uv__free(short_path_buffer);
+      short_path_buffer = NULL;
+    }
+short_path_done:
+    short_path = short_path_buffer;
+
+    if (uv__split_path(pathw, &dir, &handle->filew) != 0) {
+      last_error = GetLastError();
+      goto error;
+    }
+
+    if (uv__split_path(short_path, NULL, &handle->short_filew) != 0) {
+      last_error = GetLastError();
+      goto error;
+    }
+
+    dir_to_watch = dir;
+    uv__free(pathw);
+    pathw = NULL;
+  }
+
+  handle->dir_handle = CreateFileW(dir_to_watch,
+                                   FILE_LIST_DIRECTORY,
+                                   FILE_SHARE_READ | FILE_SHARE_DELETE |
+                                     FILE_SHARE_WRITE,
+                                   NULL,
+                                   OPEN_EXISTING,
+                                   FILE_FLAG_BACKUP_SEMANTICS |
+                                     FILE_FLAG_OVERLAPPED,
+                                   NULL);
+
+  if (dir) {
+    uv__free(dir);
+    dir = NULL;
+  }
+
+  if (handle->dir_handle == INVALID_HANDLE_VALUE) {
+    last_error = GetLastError();
+    goto error;
+  }
+
+  if (CreateIoCompletionPort(handle->dir_handle,
+                             handle->loop->iocp,
+                             (ULONG_PTR)handle,
+                             0) == NULL) {
+    last_error = GetLastError();
+    goto error;
+  }
+
+  if (!handle->buffer) {
+    handle->buffer = (char*)uv__malloc(uv_directory_watcher_buffer_size);
+  }
+  if (!handle->buffer) {
+    uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc");
+  }
+
+  memset(&(handle->req.u.io.overlapped), 0,
+         sizeof(handle->req.u.io.overlapped));
+
+  if (!ReadDirectoryChangesW(handle->dir_handle,
+                             handle->buffer,
+                             uv_directory_watcher_buffer_size,
+                             (flags & UV_FS_EVENT_RECURSIVE) ? TRUE : FALSE,
+                             FILE_NOTIFY_CHANGE_FILE_NAME      |
+                               FILE_NOTIFY_CHANGE_DIR_NAME     |
+                               FILE_NOTIFY_CHANGE_ATTRIBUTES   |
+                               FILE_NOTIFY_CHANGE_SIZE         |
+                               FILE_NOTIFY_CHANGE_LAST_WRITE   |
+                               FILE_NOTIFY_CHANGE_LAST_ACCESS  |
+                               FILE_NOTIFY_CHANGE_CREATION     |
+                               FILE_NOTIFY_CHANGE_SECURITY,
+                             NULL,
+                             &handle->req.u.io.overlapped,
+                             NULL)) {
+    last_error = GetLastError();
+    goto error;
+  }
+
+  assert(is_path_dir ? pathw != NULL : pathw == NULL);
+  handle->dirw = pathw;
+  handle->req_pending = 1;
+  return 0;
+
+error:
+  if (handle->path) {
+    uv__free(handle->path);
+    handle->path = NULL;
+  }
+
+  if (handle->filew) {
+    uv__free(handle->filew);
+    handle->filew = NULL;
+  }
+
+  if (handle->short_filew) {
+    uv__free(handle->short_filew);
+    handle->short_filew = NULL;
+  }
+
+  uv__free(pathw);
+
+  if (handle->dir_handle != INVALID_HANDLE_VALUE) {
+    CloseHandle(handle->dir_handle);
+    handle->dir_handle = INVALID_HANDLE_VALUE;
+  }
+
+  if (handle->buffer) {
+    uv__free(handle->buffer);
+    handle->buffer = NULL;
+  }
+
+  if (uv__is_active(handle))
+    uv__handle_stop(handle);
+
+  uv__free(short_path);
+
+  return uv_translate_sys_error(last_error);
+}
+
+
+int uv_fs_event_stop(uv_fs_event_t* handle) {
+  if (!uv__is_active(handle))
+    return 0;
+
+  if (handle->dir_handle != INVALID_HANDLE_VALUE) {
+    CloseHandle(handle->dir_handle);
+    handle->dir_handle = INVALID_HANDLE_VALUE;
+  }
+
+  uv__handle_stop(handle);
+
+  if (handle->filew) {
+    uv__free(handle->filew);
+    handle->filew = NULL;
+  }
+
+  if (handle->short_filew) {
+    uv__free(handle->short_filew);
+    handle->short_filew = NULL;
+  }
+
+  if (handle->path) {
+    uv__free(handle->path);
+    handle->path = NULL;
+  }
+
+  if (handle->dirw) {
+    uv__free(handle->dirw);
+    handle->dirw = NULL;
+  }
+
+  return 0;
+}
+
+
+static int file_info_cmp(WCHAR* str, WCHAR* file_name, size_t file_name_len) {
+  size_t str_len;
+
+  if (str == NULL)
+    return -1;
+
+  str_len = wcslen(str);
+
+  /*
+    Since we only care about equality, return early if the strings
+    aren't the same length
+  */
+  if (str_len != (file_name_len / sizeof(WCHAR)))
+    return -1;
+
+  return _wcsnicmp(str, file_name, str_len);
+}
+
+
+void uv__process_fs_event_req(uv_loop_t* loop, uv_req_t* req,
+    uv_fs_event_t* handle) {
+  FILE_NOTIFY_INFORMATION* file_info;
+  int err, sizew, size;
+  char* filename = NULL;
+  WCHAR* filenamew = NULL;
+  WCHAR* long_filenamew = NULL;
+  DWORD offset = 0;
+
+  assert(req->type == UV_FS_EVENT_REQ);
+  assert(handle->req_pending);
+  handle->req_pending = 0;
+
+  /* Don't report any callbacks if:
+   * - We're closing, just push the handle onto the endgame queue
+   * - We are not active, just ignore the callback
+   */
+  if (!uv__is_active(handle)) {
+    if (handle->flags & UV_HANDLE_CLOSING) {
+      uv__want_endgame(loop, (uv_handle_t*) handle);
+    }
+    return;
+  }
+
+  file_info = (FILE_NOTIFY_INFORMATION*)(handle->buffer + offset);
+
+  if (REQ_SUCCESS(req)) {
+    if (req->u.io.overlapped.InternalHigh > 0) {
+      do {
+        file_info = (FILE_NOTIFY_INFORMATION*)((char*)file_info + offset);
+        assert(!filename);
+        assert(!filenamew);
+        assert(!long_filenamew);
+
+        /*
+         * Fire the event only if we were asked to watch a directory,
+         * or if the filename filter matches.
+         */
+        if (handle->dirw ||
+            file_info_cmp(handle->filew,
+                          file_info->FileName,
+                          file_info->FileNameLength) == 0 ||
+            file_info_cmp(handle->short_filew,
+                          file_info->FileName,
+                          file_info->FileNameLength) == 0) {
+
+          if (handle->dirw) {
+            /*
+             * We attempt to resolve the long form of the file name explicitly.
+             * We only do this for file names that might still exist on disk.
+             * If this fails, we use the name given by ReadDirectoryChangesW.
+             * This may be the long form or the 8.3 short name in some cases.
+             */
+            if (file_info->Action != FILE_ACTION_REMOVED &&
+              file_info->Action != FILE_ACTION_RENAMED_OLD_NAME) {
+              /* Construct a full path to the file. */
+              size = wcslen(handle->dirw) +
+                file_info->FileNameLength / sizeof(WCHAR) + 2;
+
+              filenamew = (WCHAR*)uv__malloc(size * sizeof(WCHAR));
+              if (!filenamew) {
+                uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc");
+              }
+
+              _snwprintf(filenamew, size, L"%s\\%.*s", handle->dirw,
+                file_info->FileNameLength / (DWORD)sizeof(WCHAR),
+                file_info->FileName);
+
+              filenamew[size - 1] = L'\0';
+
+              /* Convert to long name. */
+              size = GetLongPathNameW(filenamew, NULL, 0);
+
+              if (size) {
+                long_filenamew = (WCHAR*)uv__malloc(size * sizeof(WCHAR));
+                if (!long_filenamew) {
+                  uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc");
+                }
+
+                size = GetLongPathNameW(filenamew, long_filenamew, size);
+                if (size) {
+                  long_filenamew[size] = '\0';
+                } else {
+                  uv__free(long_filenamew);
+                  long_filenamew = NULL;
+                }
+              }
+
+              uv__free(filenamew);
+
+              if (long_filenamew) {
+                /* Get the file name out of the long path. */
+                uv__relative_path(long_filenamew,
+                                  handle->dirw,
+                                  &filenamew);
+                uv__free(long_filenamew);
+                long_filenamew = filenamew;
+                sizew = -1;
+              } else {
+                /* We couldn't get the long filename, use the one reported. */
+                filenamew = file_info->FileName;
+                sizew = file_info->FileNameLength / sizeof(WCHAR);
+              }
+            } else {
+              /*
+               * Removed or renamed events cannot be resolved to the long form.
+               * We therefore use the name given by ReadDirectoryChangesW.
+               * This may be the long form or the 8.3 short name in some cases.
+               */
+              filenamew = file_info->FileName;
+              sizew = file_info->FileNameLength / sizeof(WCHAR);
+            }
+          } else {
+            /* We already have the long name of the file, so just use it. */
+            filenamew = handle->filew;
+            sizew = -1;
+          }
+
+          /* Convert the filename to utf8. */
+          uv__convert_utf16_to_utf8(filenamew, sizew, &filename);
+
+          switch (file_info->Action) {
+            case FILE_ACTION_ADDED:
+            case FILE_ACTION_REMOVED:
+            case FILE_ACTION_RENAMED_OLD_NAME:
+            case FILE_ACTION_RENAMED_NEW_NAME:
+              handle->cb(handle, filename, UV_RENAME, 0);
+              break;
+
+            case FILE_ACTION_MODIFIED:
+              handle->cb(handle, filename, UV_CHANGE, 0);
+              break;
+          }
+
+          uv__free(filename);
+          filename = NULL;
+          uv__free(long_filenamew);
+          long_filenamew = NULL;
+          filenamew = NULL;
+        }
+
+        offset = file_info->NextEntryOffset;
+      } while (offset && !(handle->flags & UV_HANDLE_CLOSING));
+    } else {
+      handle->cb(handle, NULL, UV_CHANGE, 0);
+    }
+  } else {
+    err = GET_REQ_ERROR(req);
+    handle->cb(handle, NULL, 0, uv_translate_sys_error(err));
+  }
+
+  if (handle->flags & UV_HANDLE_CLOSING) {
+    uv__want_endgame(loop, (uv_handle_t*)handle);
+  } else if (uv__is_active(handle)) {
+    uv__fs_event_queue_readdirchanges(loop, handle);
+  }
+}
+
+
+void uv__fs_event_close(uv_loop_t* loop, uv_fs_event_t* handle) {
+  uv_fs_event_stop(handle);
+
+  uv__handle_closing(handle);
+
+  if (!handle->req_pending) {
+    uv__want_endgame(loop, (uv_handle_t*)handle);
+  }
+
+}
+
+
+void uv__fs_event_endgame(uv_loop_t* loop, uv_fs_event_t* handle) {
+  if ((handle->flags & UV_HANDLE_CLOSING) && !handle->req_pending) {
+    assert(!(handle->flags & UV_HANDLE_CLOSED));
+
+    if (handle->buffer) {
+      uv__free(handle->buffer);
+      handle->buffer = NULL;
+    }
+
+    uv__handle_close(handle);
+  }
+}
diff --git a/wpinet/src/main/native/thirdparty/libuv/src/win/fs-fd-hash-inl.h b/wpinet/src/main/native/thirdparty/libuv/src/win/fs-fd-hash-inl.h
new file mode 100644
index 0000000..703a8d8
--- /dev/null
+++ b/wpinet/src/main/native/thirdparty/libuv/src/win/fs-fd-hash-inl.h
@@ -0,0 +1,200 @@
+/* Copyright libuv project contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#ifndef UV_WIN_FS_FD_HASH_INL_H_
+#define UV_WIN_FS_FD_HASH_INL_H_
+
+#include "uv.h"
+#include "internal.h"
+
+/* Files are only inserted in uv__fd_hash when the UV_FS_O_FILEMAP flag is
+ * specified. Thus, when uv__fd_hash_get returns true, the file mapping in the
+ * info structure should be used for read/write operations.
+ *
+ * If the file is empty, the mapping field will be set to
+ * INVALID_HANDLE_VALUE. This is not an issue since the file mapping needs to
+ * be created anyway when the file size changes.
+ *
+ * Since file descriptors are sequential integers, the modulo operator is used
+ * as hashing function. For each bucket, a single linked list of arrays is
+ * kept to minimize allocations. A statically allocated memory buffer is kept
+ * for the first array in each bucket. */
+
+
+#define UV__FD_HASH_SIZE 256
+#define UV__FD_HASH_GROUP_SIZE 16
+
+struct uv__fd_info_s {
+  int flags;
+  BOOLEAN is_directory;
+  HANDLE mapping;
+  LARGE_INTEGER size;
+  LARGE_INTEGER current_pos;
+};
+
+struct uv__fd_hash_entry_s {
+  uv_file fd;
+  struct uv__fd_info_s info;
+};
+
+struct uv__fd_hash_entry_group_s {
+  struct uv__fd_hash_entry_s entries[UV__FD_HASH_GROUP_SIZE];
+  struct uv__fd_hash_entry_group_s* next;
+};
+
+struct uv__fd_hash_bucket_s {
+  size_t size;
+  struct uv__fd_hash_entry_group_s* data;
+};
+
+
+static uv_mutex_t uv__fd_hash_mutex;
+
+static struct uv__fd_hash_entry_group_s
+  uv__fd_hash_entry_initial[UV__FD_HASH_SIZE * UV__FD_HASH_GROUP_SIZE];
+static struct uv__fd_hash_bucket_s uv__fd_hash[UV__FD_HASH_SIZE];
+
+
+INLINE static void uv__fd_hash_init(void) {
+  size_t i;
+  int err;
+
+  err = uv_mutex_init(&uv__fd_hash_mutex);
+  if (err) {
+    uv_fatal_error(err, "uv_mutex_init");
+  }
+
+  for (i = 0; i < ARRAY_SIZE(uv__fd_hash); ++i) {
+    uv__fd_hash[i].size = 0;
+    uv__fd_hash[i].data =
+        uv__fd_hash_entry_initial + i * UV__FD_HASH_GROUP_SIZE;
+  }
+}
+
+#define FIND_COMMON_VARIABLES                                                \
+  unsigned i;                                                                \
+  unsigned bucket = fd % ARRAY_SIZE(uv__fd_hash);                            \
+  struct uv__fd_hash_entry_s* entry_ptr = NULL;                              \
+  struct uv__fd_hash_entry_group_s* group_ptr;                               \
+  struct uv__fd_hash_bucket_s* bucket_ptr = &uv__fd_hash[bucket];
+
+#define FIND_IN_GROUP_PTR(group_size)                                        \
+  do {                                                                       \
+    for (i = 0; i < group_size; ++i) {                                       \
+      if (group_ptr->entries[i].fd == fd) {                                  \
+        entry_ptr = &group_ptr->entries[i];                                  \
+        break;                                                               \
+      }                                                                      \
+    }                                                                        \
+  } while (0)
+
+#define FIND_IN_BUCKET_PTR()                                                 \
+  do {                                                                       \
+    size_t first_group_size = bucket_ptr->size % UV__FD_HASH_GROUP_SIZE;     \
+    if (bucket_ptr->size != 0 && first_group_size == 0)                      \
+      first_group_size = UV__FD_HASH_GROUP_SIZE;                             \
+    group_ptr = bucket_ptr->data;                                            \
+    FIND_IN_GROUP_PTR(first_group_size);                                     \
+    for (group_ptr = group_ptr->next;                                        \
+         group_ptr != NULL && entry_ptr == NULL;                             \
+         group_ptr = group_ptr->next)                                        \
+      FIND_IN_GROUP_PTR(UV__FD_HASH_GROUP_SIZE);                             \
+  } while (0)
+
+INLINE static int uv__fd_hash_get(int fd, struct uv__fd_info_s* info) {
+  FIND_COMMON_VARIABLES
+
+  uv_mutex_lock(&uv__fd_hash_mutex);
+
+  FIND_IN_BUCKET_PTR();
+
+  if (entry_ptr != NULL) {
+    *info = entry_ptr->info;
+  }
+
+  uv_mutex_unlock(&uv__fd_hash_mutex);
+  return entry_ptr != NULL;
+}
+
+INLINE static void uv__fd_hash_add(int fd, struct uv__fd_info_s* info) {
+  FIND_COMMON_VARIABLES
+
+  uv_mutex_lock(&uv__fd_hash_mutex);
+
+  FIND_IN_BUCKET_PTR();
+
+  if (entry_ptr == NULL) {
+    i = bucket_ptr->size % UV__FD_HASH_GROUP_SIZE;
+
+    if (bucket_ptr->size != 0 && i == 0) {
+      struct uv__fd_hash_entry_group_s* new_group_ptr =
+        (struct uv__fd_hash_entry_group_s*)uv__malloc(sizeof(*new_group_ptr));
+      if (new_group_ptr == NULL) {
+        uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc");
+      }
+      new_group_ptr->next = bucket_ptr->data;
+      bucket_ptr->data = new_group_ptr;
+    }
+
+    bucket_ptr->size += 1;
+    entry_ptr = &bucket_ptr->data->entries[i];
+    entry_ptr->fd = fd;
+  }
+
+  entry_ptr->info = *info;
+
+  uv_mutex_unlock(&uv__fd_hash_mutex);
+}
+
+INLINE static int uv__fd_hash_remove(int fd, struct uv__fd_info_s* info) {
+  FIND_COMMON_VARIABLES
+
+  uv_mutex_lock(&uv__fd_hash_mutex);
+
+  FIND_IN_BUCKET_PTR();
+
+  if (entry_ptr != NULL) {
+    *info = entry_ptr->info;
+
+    bucket_ptr->size -= 1;
+
+    i = bucket_ptr->size % UV__FD_HASH_GROUP_SIZE;
+    if (entry_ptr != &bucket_ptr->data->entries[i]) {
+      *entry_ptr = bucket_ptr->data->entries[i];
+    }
+
+    if (bucket_ptr->size != 0 &&
+        bucket_ptr->size % UV__FD_HASH_GROUP_SIZE == 0) {
+      struct uv__fd_hash_entry_group_s* old_group_ptr = bucket_ptr->data;
+      bucket_ptr->data = old_group_ptr->next;
+      uv__free(old_group_ptr);
+    }
+  }
+
+  uv_mutex_unlock(&uv__fd_hash_mutex);
+  return entry_ptr != NULL;
+}
+
+#undef FIND_COMMON_VARIABLES
+#undef FIND_IN_GROUP_PTR
+#undef FIND_IN_BUCKET_PTR
+
+#endif /* UV_WIN_FS_FD_HASH_INL_H_ */
diff --git a/wpinet/src/main/native/thirdparty/libuv/src/win/fs.cpp b/wpinet/src/main/native/thirdparty/libuv/src/win/fs.cpp
new file mode 100644
index 0000000..71c9b16
--- /dev/null
+++ b/wpinet/src/main/native/thirdparty/libuv/src/win/fs.cpp
@@ -0,0 +1,3442 @@
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#define _CRT_NONSTDC_NO_WARNINGS
+
+#include <assert.h>
+#include <stdlib.h>
+#include <direct.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <io.h>
+#include <limits.h>
+#include <sys/stat.h>
+#include <sys/utime.h>
+#include <stdio.h>
+
+#include "uv.h"
+#include "internal.h"
+#include "req-inl.h"
+#include "handle-inl.h"
+#include "fs-fd-hash-inl.h"
+
+#pragma comment(lib, "Advapi32.lib")
+
+#define UV_FS_FREE_PATHS         0x0002
+#define UV_FS_FREE_PTR           0x0008
+#define UV_FS_CLEANEDUP          0x0010
+
+
+#define INIT(subtype)                                                         \
+  do {                                                                        \
+    if (req == NULL)                                                          \
+      return UV_EINVAL;                                                       \
+    uv__fs_req_init(loop, req, subtype, cb);                                  \
+  }                                                                           \
+  while (0)
+
+#define POST                                                                  \
+  do {                                                                        \
+    if (cb != NULL) {                                                         \
+      uv__req_register(loop, req);                                            \
+      uv__work_submit(loop,                                                   \
+                      &req->work_req,                                         \
+                      UV__WORK_FAST_IO,                                       \
+                      uv__fs_work,                                            \
+                      uv__fs_done);                                           \
+      return 0;                                                               \
+    } else {                                                                  \
+      uv__fs_work(&req->work_req);                                            \
+      return req->result;                                                     \
+    }                                                                         \
+  }                                                                           \
+  while (0)
+
+#define SET_REQ_RESULT(req, result_value)                                   \
+  do {                                                                      \
+    req->result = (result_value);                                           \
+    assert(req->result != -1);                                              \
+  } while (0)
+
+#define SET_REQ_WIN32_ERROR(req, sys_errno)                                 \
+  do {                                                                      \
+    req->sys_errno_ = (sys_errno);                                          \
+    req->result = uv_translate_sys_error(req->sys_errno_);                  \
+  } while (0)
+
+#define SET_REQ_UV_ERROR(req, uv_errno, sys_errno)                          \
+  do {                                                                      \
+    req->result = (uv_errno);                                               \
+    req->sys_errno_ = (sys_errno);                                          \
+  } while (0)
+
+#define VERIFY_FD(fd, req)                                                  \
+  if (fd == -1) {                                                           \
+    req->result = UV_EBADF;                                                 \
+    req->sys_errno_ = ERROR_INVALID_HANDLE;                                 \
+    return;                                                                 \
+  }
+
+#define MILLION ((int64_t) 1000 * 1000)
+#define BILLION ((int64_t) 1000 * 1000 * 1000)
+
+static void uv__filetime_to_timespec(uv_timespec_t *ts, int64_t filetime) {
+  filetime -= 116444736 * BILLION;
+  ts->tv_sec = (long) (filetime / (10 * MILLION));
+  ts->tv_nsec = (long) ((filetime - ts->tv_sec * 10 * MILLION) * 100U);
+  if (ts->tv_nsec < 0) {
+    ts->tv_sec -= 1;
+    ts->tv_nsec += 1e9;
+  }
+}
+
+#define TIME_T_TO_FILETIME(time, filetime_ptr)                              \
+  do {                                                                      \
+    int64_t bigtime = ((time) * 10 * MILLION + 116444736 * BILLION);        \
+    (filetime_ptr)->dwLowDateTime = (uint64_t) bigtime & 0xFFFFFFFF;        \
+    (filetime_ptr)->dwHighDateTime = (uint64_t) bigtime >> 32;              \
+  } while(0)
+
+#define IS_SLASH(c) ((c) == L'\\' || (c) == L'/')
+#define IS_LETTER(c) (((c) >= L'a' && (c) <= L'z') || \
+  ((c) >= L'A' && (c) <= L'Z'))
+
+#define MIN(a,b) (((a) < (b)) ? (a) : (b))
+
+const WCHAR JUNCTION_PREFIX[] = L"\\??\\";
+const WCHAR JUNCTION_PREFIX_LEN = 4;
+
+const WCHAR LONG_PATH_PREFIX[] = L"\\\\?\\";
+const WCHAR LONG_PATH_PREFIX_LEN = 4;
+
+const WCHAR UNC_PATH_PREFIX[] = L"\\\\?\\UNC\\";
+const WCHAR UNC_PATH_PREFIX_LEN = 8;
+
+static int uv__file_symlink_usermode_flag = SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE;
+
+static DWORD uv__allocation_granularity;
+
+
+void uv__fs_init(void) {
+  SYSTEM_INFO system_info;
+
+  GetSystemInfo(&system_info);
+  uv__allocation_granularity = system_info.dwAllocationGranularity;
+
+  uv__fd_hash_init();
+}
+
+
+INLINE static int fs__capture_path(uv_fs_t* req, const char* path,
+    const char* new_path, const int copy_path) {
+  char* buf;
+  char* pos;
+  ssize_t buf_sz = 0, path_len = 0, pathw_len = 0, new_pathw_len = 0;
+
+  /* new_path can only be set if path is also set. */
+  assert(new_path == NULL || path != NULL);
+
+  if (path != NULL) {
+    pathw_len = MultiByteToWideChar(CP_UTF8,
+                                    0,
+                                    path,
+                                    -1,
+                                    NULL,
+                                    0);
+    if (pathw_len == 0) {
+      return GetLastError();
+    }
+
+    buf_sz += pathw_len * sizeof(WCHAR);
+  }
+
+  if (path != NULL && copy_path) {
+    path_len = 1 + strlen(path);
+    buf_sz += path_len;
+  }
+
+  if (new_path != NULL) {
+    new_pathw_len = MultiByteToWideChar(CP_UTF8,
+                                        0,
+                                        new_path,
+                                        -1,
+                                        NULL,
+                                        0);
+    if (new_pathw_len == 0) {
+      return GetLastError();
+    }
+
+    buf_sz += new_pathw_len * sizeof(WCHAR);
+  }
+
+
+  if (buf_sz == 0) {
+    req->file.pathw = NULL;
+    req->fs.info.new_pathw = NULL;
+    req->path = NULL;
+    return 0;
+  }
+
+  buf = (char*) uv__malloc(buf_sz);
+  if (buf == NULL) {
+    return ERROR_OUTOFMEMORY;
+  }
+
+  pos = buf;
+
+  if (path != NULL) {
+    DWORD r = MultiByteToWideChar(CP_UTF8,
+                                  0,
+                                  path,
+                                  -1,
+                                  (WCHAR*) pos,
+                                  pathw_len);
+    assert(r == (DWORD) pathw_len);
+    req->file.pathw = (WCHAR*) pos;
+    pos += r * sizeof(WCHAR);
+  } else {
+    req->file.pathw = NULL;
+  }
+
+  if (new_path != NULL) {
+    DWORD r = MultiByteToWideChar(CP_UTF8,
+                                  0,
+                                  new_path,
+                                  -1,
+                                  (WCHAR*) pos,
+                                  new_pathw_len);
+    assert(r == (DWORD) new_pathw_len);
+    req->fs.info.new_pathw = (WCHAR*) pos;
+    pos += r * sizeof(WCHAR);
+  } else {
+    req->fs.info.new_pathw = NULL;
+  }
+
+  req->path = path;
+  if (path != NULL && copy_path) {
+    memcpy(pos, path, path_len);
+    assert(path_len == buf_sz - (pos - buf));
+    req->path = pos;
+  }
+
+  req->flags |= UV_FS_FREE_PATHS;
+
+  return 0;
+}
+
+
+
+INLINE static void uv__fs_req_init(uv_loop_t* loop, uv_fs_t* req,
+    uv_fs_type fs_type, const uv_fs_cb cb) {
+  uv__once_init();
+  UV_REQ_INIT(req, UV_FS);
+  req->loop = loop;
+  req->flags = 0;
+  req->fs_type = fs_type;
+  req->sys_errno_ = 0;
+  req->result = 0;
+  req->ptr = NULL;
+  req->path = NULL;
+  req->cb = cb;
+  memset(&req->fs, 0, sizeof(req->fs));
+}
+
+
+static int fs__wide_to_utf8(WCHAR* w_source_ptr,
+                               DWORD w_source_len,
+                               char** target_ptr,
+                               uint64_t* target_len_ptr) {
+  int r;
+  int target_len;
+  char* target;
+  target_len = WideCharToMultiByte(CP_UTF8,
+                                   0,
+                                   w_source_ptr,
+                                   w_source_len,
+                                   NULL,
+                                   0,
+                                   NULL,
+                                   NULL);
+
+  if (target_len == 0) {
+    return -1;
+  }
+
+  if (target_len_ptr != NULL) {
+    *target_len_ptr = target_len;
+  }
+
+  if (target_ptr == NULL) {
+    return 0;
+  }
+
+  target = (char*)uv__malloc(target_len + 1);
+  if (target == NULL) {
+    SetLastError(ERROR_OUTOFMEMORY);
+    return -1;
+  }
+
+  r = WideCharToMultiByte(CP_UTF8,
+                          0,
+                          w_source_ptr,
+                          w_source_len,
+                          target,
+                          target_len,
+                          NULL,
+                          NULL);
+  assert(r == target_len);
+  target[target_len] = '\0';
+  *target_ptr = target;
+  return 0;
+}
+
+
+INLINE static int fs__readlink_handle(HANDLE handle, char** target_ptr,
+    uint64_t* target_len_ptr) {
+  char buffer[MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
+  REPARSE_DATA_BUFFER* reparse_data = (REPARSE_DATA_BUFFER*) buffer;
+  WCHAR* w_target;
+  DWORD w_target_len;
+  DWORD bytes;
+  size_t i;
+  size_t len;
+
+  if (!DeviceIoControl(handle,
+                       FSCTL_GET_REPARSE_POINT,
+                       NULL,
+                       0,
+                       buffer,
+                       sizeof buffer,
+                       &bytes,
+                       NULL)) {
+    return -1;
+  }
+
+  if (reparse_data->ReparseTag == IO_REPARSE_TAG_SYMLINK) {
+    /* Real symlink */
+    w_target = reparse_data->SymbolicLinkReparseBuffer.PathBuffer +
+        (reparse_data->SymbolicLinkReparseBuffer.SubstituteNameOffset /
+        sizeof(WCHAR));
+    w_target_len =
+        reparse_data->SymbolicLinkReparseBuffer.SubstituteNameLength /
+        sizeof(WCHAR);
+
+    /* Real symlinks can contain pretty much everything, but the only thing we
+     * really care about is undoing the implicit conversion to an NT namespaced
+     * path that CreateSymbolicLink will perform on absolute paths. If the path
+     * is win32-namespaced then the user must have explicitly made it so, and
+     * we better just return the unmodified reparse data. */
+    if (w_target_len >= 4 &&
+        w_target[0] == L'\\' &&
+        w_target[1] == L'?' &&
+        w_target[2] == L'?' &&
+        w_target[3] == L'\\') {
+      /* Starts with \??\ */
+      if (w_target_len >= 6 &&
+          ((w_target[4] >= L'A' && w_target[4] <= L'Z') ||
+           (w_target[4] >= L'a' && w_target[4] <= L'z')) &&
+          w_target[5] == L':' &&
+          (w_target_len == 6 || w_target[6] == L'\\')) {
+        /* \??\<drive>:\ */
+        w_target += 4;
+        w_target_len -= 4;
+
+      } else if (w_target_len >= 8 &&
+                 (w_target[4] == L'U' || w_target[4] == L'u') &&
+                 (w_target[5] == L'N' || w_target[5] == L'n') &&
+                 (w_target[6] == L'C' || w_target[6] == L'c') &&
+                 w_target[7] == L'\\') {
+        /* \??\UNC\<server>\<share>\ - make sure the final path looks like
+         * \\<server>\<share>\ */
+        w_target += 6;
+        w_target[0] = L'\\';
+        w_target_len -= 6;
+      }
+    }
+
+  } else if (reparse_data->ReparseTag == IO_REPARSE_TAG_MOUNT_POINT) {
+    /* Junction. */
+    w_target = reparse_data->MountPointReparseBuffer.PathBuffer +
+        (reparse_data->MountPointReparseBuffer.SubstituteNameOffset /
+        sizeof(WCHAR));
+    w_target_len = reparse_data->MountPointReparseBuffer.SubstituteNameLength /
+        sizeof(WCHAR);
+
+    /* Only treat junctions that look like \??\<drive>:\ as symlink. Junctions
+     * can also be used as mount points, like \??\Volume{<guid>}, but that's
+     * confusing for programs since they wouldn't be able to actually
+     * understand such a path when returned by uv_readlink(). UNC paths are
+     * never valid for junctions so we don't care about them. */
+    if (!(w_target_len >= 6 &&
+          w_target[0] == L'\\' &&
+          w_target[1] == L'?' &&
+          w_target[2] == L'?' &&
+          w_target[3] == L'\\' &&
+          ((w_target[4] >= L'A' && w_target[4] <= L'Z') ||
+           (w_target[4] >= L'a' && w_target[4] <= L'z')) &&
+          w_target[5] == L':' &&
+          (w_target_len == 6 || w_target[6] == L'\\'))) {
+      SetLastError(ERROR_SYMLINK_NOT_SUPPORTED);
+      return -1;
+    }
+
+    /* Remove leading \??\ */
+    w_target += 4;
+    w_target_len -= 4;
+
+  } else if (reparse_data->ReparseTag == IO_REPARSE_TAG_APPEXECLINK) {
+    /* String #3 in the list has the target filename. */
+    if (reparse_data->AppExecLinkReparseBuffer.StringCount < 3) {
+      SetLastError(ERROR_SYMLINK_NOT_SUPPORTED);
+      return -1;
+    }
+    w_target = reparse_data->AppExecLinkReparseBuffer.StringList;
+    /* The StringList buffer contains a list of strings separated by "\0",   */
+    /* with "\0\0" terminating the list. Move to the 3rd string in the list: */
+    for (i = 0; i < 2; ++i) {
+      len = wcslen(w_target);
+      if (len == 0) {
+        SetLastError(ERROR_SYMLINK_NOT_SUPPORTED);
+        return -1;
+      }
+      w_target += len + 1;
+    }
+    w_target_len = wcslen(w_target);
+    if (w_target_len == 0) {
+      SetLastError(ERROR_SYMLINK_NOT_SUPPORTED);
+      return -1;
+    }
+    /* Make sure it is an absolute path. */
+    if (!(w_target_len >= 3 &&
+         ((w_target[0] >= L'a' && w_target[0] <= L'z') ||
+          (w_target[0] >= L'A' && w_target[0] <= L'Z')) &&
+         w_target[1] == L':' &&
+         w_target[2] == L'\\')) {
+      SetLastError(ERROR_SYMLINK_NOT_SUPPORTED);
+      return -1;
+    }
+
+  } else {
+    /* Reparse tag does not indicate a symlink. */
+    SetLastError(ERROR_SYMLINK_NOT_SUPPORTED);
+    return -1;
+  }
+
+  return fs__wide_to_utf8(w_target, w_target_len, target_ptr, target_len_ptr);
+}
+
+
+void fs__open(uv_fs_t* req) {
+  DWORD access;
+  DWORD share;
+  DWORD disposition;
+  DWORD attributes = 0;
+  HANDLE file;
+  int fd, current_umask;
+  int flags = req->fs.info.file_flags;
+  struct uv__fd_info_s fd_info;
+
+  /* Adjust flags to be compatible with the memory file mapping. Save the
+   * original flags to emulate the correct behavior. */
+  if (flags & UV_FS_O_FILEMAP) {
+    fd_info.flags = flags;
+    fd_info.current_pos.QuadPart = 0;
+
+    if ((flags & (UV_FS_O_RDONLY | UV_FS_O_WRONLY | UV_FS_O_RDWR)) ==
+        UV_FS_O_WRONLY) {
+      /* CreateFileMapping always needs read access */
+      flags = (flags & ~UV_FS_O_WRONLY) | UV_FS_O_RDWR;
+    }
+
+    if (flags & UV_FS_O_APPEND) {
+      /* Clear the append flag and ensure RDRW mode */
+      flags &= ~UV_FS_O_APPEND;
+      flags &= ~(UV_FS_O_RDONLY | UV_FS_O_WRONLY | UV_FS_O_RDWR);
+      flags |= UV_FS_O_RDWR;
+    }
+  }
+
+  /* Obtain the active umask. umask() never fails and returns the previous
+   * umask. */
+  current_umask = umask(0);
+  umask(current_umask);
+
+  /* convert flags and mode to CreateFile parameters */
+  switch (flags & (UV_FS_O_RDONLY | UV_FS_O_WRONLY | UV_FS_O_RDWR)) {
+  case UV_FS_O_RDONLY:
+    access = FILE_GENERIC_READ;
+    break;
+  case UV_FS_O_WRONLY:
+    access = FILE_GENERIC_WRITE;
+    break;
+  case UV_FS_O_RDWR:
+    access = FILE_GENERIC_READ | FILE_GENERIC_WRITE;
+    break;
+  default:
+    goto einval;
+  }
+
+  if (flags & UV_FS_O_APPEND) {
+    access &= ~FILE_WRITE_DATA;
+    access |= FILE_APPEND_DATA;
+  }
+
+  /*
+   * Here is where we deviate significantly from what CRT's _open()
+   * does. We indiscriminately use all the sharing modes, to match
+   * UNIX semantics. In particular, this ensures that the file can
+   * be deleted even whilst it's open, fixing issue
+   * https://github.com/nodejs/node-v0.x-archive/issues/1449.
+   * We still support exclusive sharing mode, since it is necessary
+   * for opening raw block devices, otherwise Windows will prevent
+   * any attempt to write past the master boot record.
+   */
+  if (flags & UV_FS_O_EXLOCK) {
+    share = 0;
+  } else {
+    share = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
+  }
+
+  switch (flags & (UV_FS_O_CREAT | UV_FS_O_EXCL | UV_FS_O_TRUNC)) {
+  case 0:
+  case UV_FS_O_EXCL:
+    disposition = OPEN_EXISTING;
+    break;
+  case UV_FS_O_CREAT:
+    disposition = OPEN_ALWAYS;
+    break;
+  case UV_FS_O_CREAT | UV_FS_O_EXCL:
+  case UV_FS_O_CREAT | UV_FS_O_TRUNC | UV_FS_O_EXCL:
+    disposition = CREATE_NEW;
+    break;
+  case UV_FS_O_TRUNC:
+  case UV_FS_O_TRUNC | UV_FS_O_EXCL:
+    disposition = TRUNCATE_EXISTING;
+    break;
+  case UV_FS_O_CREAT | UV_FS_O_TRUNC:
+    disposition = CREATE_ALWAYS;
+    break;
+  default:
+    goto einval;
+  }
+
+  attributes |= FILE_ATTRIBUTE_NORMAL;
+  if (flags & UV_FS_O_CREAT) {
+    if (!((req->fs.info.mode & ~current_umask) & _S_IWRITE)) {
+      attributes |= FILE_ATTRIBUTE_READONLY;
+    }
+  }
+
+  if (flags & UV_FS_O_TEMPORARY ) {
+    attributes |= FILE_FLAG_DELETE_ON_CLOSE | FILE_ATTRIBUTE_TEMPORARY;
+    access |= DELETE;
+  }
+
+  if (flags & UV_FS_O_SHORT_LIVED) {
+    attributes |= FILE_ATTRIBUTE_TEMPORARY;
+  }
+
+  switch (flags & (UV_FS_O_SEQUENTIAL | UV_FS_O_RANDOM)) {
+  case 0:
+    break;
+  case UV_FS_O_SEQUENTIAL:
+    attributes |= FILE_FLAG_SEQUENTIAL_SCAN;
+    break;
+  case UV_FS_O_RANDOM:
+    attributes |= FILE_FLAG_RANDOM_ACCESS;
+    break;
+  default:
+    goto einval;
+  }
+
+  if (flags & UV_FS_O_DIRECT) {
+    /*
+     * FILE_APPEND_DATA and FILE_FLAG_NO_BUFFERING are mutually exclusive.
+     * Windows returns 87, ERROR_INVALID_PARAMETER if these are combined.
+     *
+     * FILE_APPEND_DATA is included in FILE_GENERIC_WRITE:
+     *
+     * FILE_GENERIC_WRITE = STANDARD_RIGHTS_WRITE |
+     *                      FILE_WRITE_DATA |
+     *                      FILE_WRITE_ATTRIBUTES |
+     *                      FILE_WRITE_EA |
+     *                      FILE_APPEND_DATA |
+     *                      SYNCHRONIZE
+     *
+     * Note: Appends are also permitted by FILE_WRITE_DATA.
+     *
+     * In order for direct writes and direct appends to succeed, we therefore
+     * exclude FILE_APPEND_DATA if FILE_WRITE_DATA is specified, and otherwise
+     * fail if the user's sole permission is a direct append, since this
+     * particular combination is invalid.
+     */
+    if (access & FILE_APPEND_DATA) {
+      if (access & FILE_WRITE_DATA) {
+        access &= ~FILE_APPEND_DATA;
+      } else {
+        goto einval;
+      }
+    }
+    attributes |= FILE_FLAG_NO_BUFFERING;
+  }
+
+  switch (flags & (UV_FS_O_DSYNC | UV_FS_O_SYNC)) {
+  case 0:
+    break;
+  case UV_FS_O_DSYNC:
+  case UV_FS_O_SYNC:
+    attributes |= FILE_FLAG_WRITE_THROUGH;
+    break;
+  default:
+    goto einval;
+  }
+
+  /* Setting this flag makes it possible to open a directory. */
+  attributes |= FILE_FLAG_BACKUP_SEMANTICS;
+
+  file = CreateFileW(req->file.pathw,
+                     access,
+                     share,
+                     NULL,
+                     disposition,
+                     attributes,
+                     NULL);
+  if (file == INVALID_HANDLE_VALUE) {
+    DWORD error = GetLastError();
+    if (error == ERROR_FILE_EXISTS && (flags & UV_FS_O_CREAT) &&
+        !(flags & UV_FS_O_EXCL)) {
+      /* Special case: when ERROR_FILE_EXISTS happens and UV_FS_O_CREAT was
+       * specified, it means the path referred to a directory. */
+      SET_REQ_UV_ERROR(req, UV_EISDIR, error);
+    } else {
+      SET_REQ_WIN32_ERROR(req, GetLastError());
+    }
+    return;
+  }
+
+  fd = _open_osfhandle((intptr_t) file, flags);
+  if (fd < 0) {
+    /* The only known failure mode for _open_osfhandle() is EMFILE, in which
+     * case GetLastError() will return zero. However we'll try to handle other
+     * errors as well, should they ever occur.
+     */
+    if (errno == EMFILE)
+      SET_REQ_UV_ERROR(req, UV_EMFILE, ERROR_TOO_MANY_OPEN_FILES);
+    else if (GetLastError() != ERROR_SUCCESS)
+      SET_REQ_WIN32_ERROR(req, GetLastError());
+    else
+      SET_REQ_WIN32_ERROR(req, (DWORD) UV_UNKNOWN);
+    CloseHandle(file);
+    return;
+  }
+
+  if (flags & UV_FS_O_FILEMAP) {
+    FILE_STANDARD_INFO file_info;
+    if (!GetFileInformationByHandleEx(file,
+                                      FileStandardInfo,
+                                      &file_info,
+                                      sizeof file_info)) {
+      SET_REQ_WIN32_ERROR(req, GetLastError());
+      CloseHandle(file);
+      return;
+    }
+    fd_info.is_directory = file_info.Directory;
+
+    if (fd_info.is_directory) {
+      fd_info.size.QuadPart = 0;
+      fd_info.mapping = INVALID_HANDLE_VALUE;
+    } else {
+      if (!GetFileSizeEx(file, &fd_info.size)) {
+        SET_REQ_WIN32_ERROR(req, GetLastError());
+        CloseHandle(file);
+        return;
+      }
+
+      if (fd_info.size.QuadPart == 0) {
+        fd_info.mapping = INVALID_HANDLE_VALUE;
+      } else {
+        DWORD flProtect = (fd_info.flags & (UV_FS_O_RDONLY | UV_FS_O_WRONLY |
+          UV_FS_O_RDWR)) == UV_FS_O_RDONLY ? PAGE_READONLY : PAGE_READWRITE;
+        fd_info.mapping = CreateFileMapping(file,
+                                            NULL,
+                                            flProtect,
+                                            fd_info.size.HighPart,
+                                            fd_info.size.LowPart,
+                                            NULL);
+        if (fd_info.mapping == NULL) {
+          SET_REQ_WIN32_ERROR(req, GetLastError());
+          CloseHandle(file);
+          return;
+        }
+      }
+    }
+
+    uv__fd_hash_add(fd, &fd_info);
+  }
+
+  SET_REQ_RESULT(req, fd);
+  return;
+
+ einval:
+  SET_REQ_UV_ERROR(req, UV_EINVAL, ERROR_INVALID_PARAMETER);
+}
+
+void fs__close(uv_fs_t* req) {
+  int fd = req->file.fd;
+  int result;
+  struct uv__fd_info_s fd_info;
+
+  VERIFY_FD(fd, req);
+
+  if (uv__fd_hash_remove(fd, &fd_info)) {
+    if (fd_info.mapping != INVALID_HANDLE_VALUE) {
+      CloseHandle(fd_info.mapping);
+    }
+  }
+
+  if (fd > 2)
+    result = _close(fd);
+  else
+    result = 0;
+
+  /* _close doesn't set _doserrno on failure, but it does always set errno
+   * to EBADF on failure.
+   */
+  if (result == -1) {
+    assert(errno == EBADF);
+    SET_REQ_UV_ERROR(req, UV_EBADF, ERROR_INVALID_HANDLE);
+  } else {
+    SET_REQ_RESULT(req, 0);
+  }
+}
+
+
+LONG fs__filemap_ex_filter(LONG excode, PEXCEPTION_POINTERS pep,
+                           int* perror) {
+  if (excode != (LONG)EXCEPTION_IN_PAGE_ERROR) {
+    return EXCEPTION_CONTINUE_SEARCH;
+  }
+
+  assert(perror != NULL);
+  if (pep != NULL && pep->ExceptionRecord != NULL &&
+      pep->ExceptionRecord->NumberParameters >= 3) {
+    NTSTATUS status = (NTSTATUS)pep->ExceptionRecord->ExceptionInformation[3];
+    *perror = pRtlNtStatusToDosError(status);
+    if (*perror != ERROR_SUCCESS) {
+      return EXCEPTION_EXECUTE_HANDLER;
+    }
+  }
+  *perror = UV_UNKNOWN;
+  return EXCEPTION_EXECUTE_HANDLER;
+}
+
+
+void fs__read_filemap(uv_fs_t* req, struct uv__fd_info_s* fd_info) {
+  int fd = req->file.fd; /* VERIFY_FD done in fs__read */
+  int rw_flags = fd_info->flags &
+    (UV_FS_O_RDONLY | UV_FS_O_WRONLY | UV_FS_O_RDWR);
+  size_t read_size, done_read;
+  unsigned int index;
+  LARGE_INTEGER pos, end_pos;
+  size_t view_offset;
+  LARGE_INTEGER view_base;
+  void* view;
+
+  if (rw_flags == UV_FS_O_WRONLY) {
+    SET_REQ_WIN32_ERROR(req, ERROR_INVALID_FLAGS);
+    return;
+  }
+  if (fd_info->is_directory) {
+    SET_REQ_WIN32_ERROR(req, ERROR_INVALID_FUNCTION);
+    return;
+  }
+
+  if (req->fs.info.offset == -1) {
+    pos = fd_info->current_pos;
+  } else {
+    pos.QuadPart = req->fs.info.offset;
+  }
+
+  /* Make sure we wont read past EOF. */
+  if (pos.QuadPart >= fd_info->size.QuadPart) {
+    SET_REQ_RESULT(req, 0);
+    return;
+  }
+
+  read_size = 0;
+  for (index = 0; index < req->fs.info.nbufs; ++index) {
+    read_size += req->fs.info.bufs[index].len;
+  }
+  read_size = (size_t) MIN((LONGLONG) read_size,
+                           fd_info->size.QuadPart - pos.QuadPart);
+  if (read_size == 0) {
+    SET_REQ_RESULT(req, 0);
+    return;
+  }
+
+  end_pos.QuadPart = pos.QuadPart + read_size;
+
+  view_offset = pos.QuadPart % uv__allocation_granularity;
+  view_base.QuadPart = pos.QuadPart - view_offset;
+  view = MapViewOfFile(fd_info->mapping,
+                       FILE_MAP_READ,
+                       view_base.HighPart,
+                       view_base.LowPart,
+                       view_offset + read_size);
+  if (view == NULL) {
+    SET_REQ_WIN32_ERROR(req, GetLastError());
+    return;
+  }
+
+  done_read = 0;
+  for (index = 0;
+       index < req->fs.info.nbufs && done_read < read_size;
+       ++index) {
+    size_t this_read_size = MIN(req->fs.info.bufs[index].len,
+                                read_size - done_read);
+#ifdef _MSC_VER
+    int err = 0;
+    __try {
+#endif
+      memcpy(req->fs.info.bufs[index].base,
+             (char*)view + view_offset + done_read,
+             this_read_size);
+#ifdef _MSC_VER
+    }
+    __except (fs__filemap_ex_filter(GetExceptionCode(),
+                                    GetExceptionInformation(), &err)) {
+      SET_REQ_WIN32_ERROR(req, err);
+      UnmapViewOfFile(view);
+      return;
+    }
+#endif
+    done_read += this_read_size;
+  }
+  assert(done_read == read_size);
+
+  if (!UnmapViewOfFile(view)) {
+    SET_REQ_WIN32_ERROR(req, GetLastError());
+    return;
+  }
+
+  if (req->fs.info.offset == -1) {
+    fd_info->current_pos = end_pos;
+    uv__fd_hash_add(fd, fd_info);
+  }
+
+  SET_REQ_RESULT(req, read_size);
+  return;
+}
+
+void fs__read(uv_fs_t* req) {
+  int fd = req->file.fd;
+  int64_t offset = req->fs.info.offset;
+  HANDLE handle;
+  OVERLAPPED overlapped, *overlapped_ptr;
+  LARGE_INTEGER offset_;
+  DWORD bytes;
+  DWORD error;
+  int result;
+  unsigned int index;
+  LARGE_INTEGER original_position;
+  LARGE_INTEGER zero_offset;
+  int restore_position;
+  struct uv__fd_info_s fd_info;
+
+  VERIFY_FD(fd, req);
+
+  if (uv__fd_hash_get(fd, &fd_info)) {
+    fs__read_filemap(req, &fd_info);
+    return;
+  }
+
+  zero_offset.QuadPart = 0;
+  restore_position = 0;
+  handle = uv__get_osfhandle(fd);
+
+  if (handle == INVALID_HANDLE_VALUE) {
+    SET_REQ_WIN32_ERROR(req, ERROR_INVALID_HANDLE);
+    return;
+  }
+
+  if (offset != -1) {
+    memset(&overlapped, 0, sizeof overlapped);
+    overlapped_ptr = &overlapped;
+    if (SetFilePointerEx(handle, zero_offset, &original_position,
+                         FILE_CURRENT)) {
+      restore_position = 1;
+    }
+  } else {
+    overlapped_ptr = NULL;
+  }
+
+  index = 0;
+  bytes = 0;
+  do {
+    DWORD incremental_bytes;
+
+    if (offset != -1) {
+      offset_.QuadPart = offset + bytes;
+      overlapped.Offset = offset_.LowPart;
+      overlapped.OffsetHigh = offset_.HighPart;
+    }
+
+    result = ReadFile(handle,
+                      req->fs.info.bufs[index].base,
+                      req->fs.info.bufs[index].len,
+                      &incremental_bytes,
+                      overlapped_ptr);
+    bytes += incremental_bytes;
+    ++index;
+  } while (result && index < req->fs.info.nbufs);
+
+  if (restore_position)
+    SetFilePointerEx(handle, original_position, NULL, FILE_BEGIN);
+
+  if (result || bytes > 0) {
+    SET_REQ_RESULT(req, bytes);
+  } else {
+    error = GetLastError();
+    if (error == ERROR_ACCESS_DENIED) {
+      error = ERROR_INVALID_FLAGS;
+    }
+
+    if (error == ERROR_HANDLE_EOF || error == ERROR_BROKEN_PIPE) {
+      SET_REQ_RESULT(req, bytes);
+    } else {
+      SET_REQ_WIN32_ERROR(req, error);
+    }
+  }
+}
+
+
+void fs__write_filemap(uv_fs_t* req, HANDLE file,
+                       struct uv__fd_info_s* fd_info) {
+  int fd = req->file.fd; /* VERIFY_FD done in fs__write */
+  int force_append = fd_info->flags & UV_FS_O_APPEND;
+  int rw_flags = fd_info->flags &
+    (UV_FS_O_RDONLY | UV_FS_O_WRONLY | UV_FS_O_RDWR);
+  size_t write_size, done_write;
+  unsigned int index;
+  LARGE_INTEGER pos, end_pos;
+  size_t view_offset;
+  LARGE_INTEGER view_base;
+  void* view;
+  FILETIME ft;
+
+  if (rw_flags == UV_FS_O_RDONLY) {
+    SET_REQ_WIN32_ERROR(req, ERROR_INVALID_FLAGS);
+    return;
+  }
+  if (fd_info->is_directory) {
+    SET_REQ_WIN32_ERROR(req, ERROR_INVALID_FUNCTION);
+    return;
+  }
+
+  write_size = 0;
+  for (index = 0; index < req->fs.info.nbufs; ++index) {
+    write_size += req->fs.info.bufs[index].len;
+  }
+
+  if (write_size == 0) {
+    SET_REQ_RESULT(req, 0);
+    return;
+  }
+
+  if (force_append) {
+    pos = fd_info->size;
+  } else if (req->fs.info.offset == -1) {
+    pos = fd_info->current_pos;
+  } else {
+    pos.QuadPart = req->fs.info.offset;
+  }
+
+  end_pos.QuadPart = pos.QuadPart + write_size;
+
+  /* Recreate the mapping to enlarge the file if needed */
+  if (end_pos.QuadPart > fd_info->size.QuadPart) {
+    if (fd_info->mapping != INVALID_HANDLE_VALUE) {
+      CloseHandle(fd_info->mapping);
+    }
+
+    fd_info->mapping = CreateFileMapping(file,
+                                         NULL,
+                                         PAGE_READWRITE,
+                                         end_pos.HighPart,
+                                         end_pos.LowPart,
+                                         NULL);
+    if (fd_info->mapping == NULL) {
+      SET_REQ_WIN32_ERROR(req, GetLastError());
+      CloseHandle(file);
+      fd_info->mapping = INVALID_HANDLE_VALUE;
+      fd_info->size.QuadPart = 0;
+      fd_info->current_pos.QuadPart = 0;
+      uv__fd_hash_add(fd, fd_info);
+      return;
+    }
+
+    fd_info->size = end_pos;
+    uv__fd_hash_add(fd, fd_info);
+  }
+
+  view_offset = pos.QuadPart % uv__allocation_granularity;
+  view_base.QuadPart = pos.QuadPart - view_offset;
+  view = MapViewOfFile(fd_info->mapping,
+                       FILE_MAP_WRITE,
+                       view_base.HighPart,
+                       view_base.LowPart,
+                       view_offset + write_size);
+  if (view == NULL) {
+    SET_REQ_WIN32_ERROR(req, GetLastError());
+    return;
+  }
+
+  done_write = 0;
+  for (index = 0; index < req->fs.info.nbufs; ++index) {
+#ifdef _MSC_VER
+    int err = 0;
+    __try {
+#endif
+      memcpy((char*)view + view_offset + done_write,
+             req->fs.info.bufs[index].base,
+             req->fs.info.bufs[index].len);
+#ifdef _MSC_VER
+    }
+    __except (fs__filemap_ex_filter(GetExceptionCode(),
+                                    GetExceptionInformation(), &err)) {
+      SET_REQ_WIN32_ERROR(req, err);
+      UnmapViewOfFile(view);
+      return;
+    }
+#endif
+    done_write += req->fs.info.bufs[index].len;
+  }
+  assert(done_write == write_size);
+
+  if (!FlushViewOfFile(view, 0)) {
+    SET_REQ_WIN32_ERROR(req, GetLastError());
+    UnmapViewOfFile(view);
+    return;
+  }
+  if (!UnmapViewOfFile(view)) {
+    SET_REQ_WIN32_ERROR(req, GetLastError());
+    return;
+  }
+
+  if (req->fs.info.offset == -1) {
+    fd_info->current_pos = end_pos;
+    uv__fd_hash_add(fd, fd_info);
+  }
+
+  GetSystemTimeAsFileTime(&ft);
+  SetFileTime(file, NULL, NULL, &ft);
+
+  SET_REQ_RESULT(req, done_write);
+}
+
+void fs__write(uv_fs_t* req) {
+  int fd = req->file.fd;
+  int64_t offset = req->fs.info.offset;
+  HANDLE handle;
+  OVERLAPPED overlapped, *overlapped_ptr;
+  LARGE_INTEGER offset_;
+  DWORD bytes;
+  DWORD error;
+  int result;
+  unsigned int index;
+  LARGE_INTEGER original_position;
+  LARGE_INTEGER zero_offset;
+  int restore_position;
+  struct uv__fd_info_s fd_info;
+
+  VERIFY_FD(fd, req);
+
+  zero_offset.QuadPart = 0;
+  restore_position = 0;
+  handle = uv__get_osfhandle(fd);
+  if (handle == INVALID_HANDLE_VALUE) {
+    SET_REQ_WIN32_ERROR(req, ERROR_INVALID_HANDLE);
+    return;
+  }
+
+  if (uv__fd_hash_get(fd, &fd_info)) {
+    fs__write_filemap(req, handle, &fd_info);
+    return;
+  }
+
+  if (offset != -1) {
+    memset(&overlapped, 0, sizeof overlapped);
+    overlapped_ptr = &overlapped;
+    if (SetFilePointerEx(handle, zero_offset, &original_position,
+                         FILE_CURRENT)) {
+      restore_position = 1;
+    }
+  } else {
+    overlapped_ptr = NULL;
+  }
+
+  index = 0;
+  bytes = 0;
+  do {
+    DWORD incremental_bytes;
+
+    if (offset != -1) {
+      offset_.QuadPart = offset + bytes;
+      overlapped.Offset = offset_.LowPart;
+      overlapped.OffsetHigh = offset_.HighPart;
+    }
+
+    result = WriteFile(handle,
+                       req->fs.info.bufs[index].base,
+                       req->fs.info.bufs[index].len,
+                       &incremental_bytes,
+                       overlapped_ptr);
+    bytes += incremental_bytes;
+    ++index;
+  } while (result && index < req->fs.info.nbufs);
+
+  if (restore_position)
+    SetFilePointerEx(handle, original_position, NULL, FILE_BEGIN);
+
+  if (result || bytes > 0) {
+    SET_REQ_RESULT(req, bytes);
+  } else {
+    error = GetLastError();
+
+    if (error == ERROR_ACCESS_DENIED) {
+      error = ERROR_INVALID_FLAGS;
+    }
+
+    SET_REQ_WIN32_ERROR(req, error);
+  }
+}
+
+
+void fs__rmdir(uv_fs_t* req) {
+  int result = _wrmdir(req->file.pathw);
+  if (result == -1)
+    SET_REQ_WIN32_ERROR(req, _doserrno);
+  else
+    SET_REQ_RESULT(req, 0);
+}
+
+
+void fs__unlink(uv_fs_t* req) {
+  const WCHAR* pathw = req->file.pathw;
+  HANDLE handle;
+  BY_HANDLE_FILE_INFORMATION info;
+  FILE_DISPOSITION_INFORMATION disposition;
+  IO_STATUS_BLOCK iosb;
+  NTSTATUS status;
+
+  handle = CreateFileW(pathw,
+                       FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES | DELETE,
+                       FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+                       NULL,
+                       OPEN_EXISTING,
+                       FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS,
+                       NULL);
+
+  if (handle == INVALID_HANDLE_VALUE) {
+    SET_REQ_WIN32_ERROR(req, GetLastError());
+    return;
+  }
+
+  if (!GetFileInformationByHandle(handle, &info)) {
+    SET_REQ_WIN32_ERROR(req, GetLastError());
+    CloseHandle(handle);
+    return;
+  }
+
+  if (info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
+    /* Do not allow deletion of directories, unless it is a symlink. When the
+     * path refers to a non-symlink directory, report EPERM as mandated by
+     * POSIX.1. */
+
+    /* Check if it is a reparse point. If it's not, it's a normal directory. */
+    if (!(info.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) {
+      SET_REQ_WIN32_ERROR(req, ERROR_ACCESS_DENIED);
+      CloseHandle(handle);
+      return;
+    }
+
+    /* Read the reparse point and check if it is a valid symlink. If not, don't
+     * unlink. */
+    if (fs__readlink_handle(handle, NULL, NULL) < 0) {
+      DWORD error = GetLastError();
+      if (error == ERROR_SYMLINK_NOT_SUPPORTED)
+        error = ERROR_ACCESS_DENIED;
+      SET_REQ_WIN32_ERROR(req, error);
+      CloseHandle(handle);
+      return;
+    }
+  }
+
+  if (info.dwFileAttributes & FILE_ATTRIBUTE_READONLY) {
+    /* Remove read-only attribute */
+    FILE_BASIC_INFORMATION basic = { 0 };
+
+    basic.FileAttributes = (info.dwFileAttributes & ~FILE_ATTRIBUTE_READONLY) |
+                           FILE_ATTRIBUTE_ARCHIVE;
+
+    status = pNtSetInformationFile(handle,
+                                   &iosb,
+                                   &basic,
+                                   sizeof basic,
+                                   FileBasicInformation);
+    if (!NT_SUCCESS(status)) {
+      SET_REQ_WIN32_ERROR(req, pRtlNtStatusToDosError(status));
+      CloseHandle(handle);
+      return;
+    }
+  }
+
+  /* Try to set the delete flag. */
+  disposition.DeleteFile = TRUE;
+  status = pNtSetInformationFile(handle,
+                                 &iosb,
+                                 &disposition,
+                                 sizeof disposition,
+                                 FileDispositionInformation);
+  if (NT_SUCCESS(status)) {
+    SET_REQ_SUCCESS(req);
+  } else {
+    SET_REQ_WIN32_ERROR(req, pRtlNtStatusToDosError(status));
+  }
+
+  CloseHandle(handle);
+}
+
+
+void fs__mkdir(uv_fs_t* req) {
+  /* TODO: use req->mode. */
+  if (CreateDirectoryW(req->file.pathw, NULL)) {
+    SET_REQ_RESULT(req, 0);
+  } else {
+    SET_REQ_WIN32_ERROR(req, GetLastError());
+    if (req->sys_errno_ == ERROR_INVALID_NAME ||
+        req->sys_errno_ == ERROR_DIRECTORY)
+      req->result = UV_EINVAL;
+  }
+}
+
+typedef int (*uv__fs_mktemp_func)(uv_fs_t* req);
+
+/* OpenBSD original: lib/libc/stdio/mktemp.c */
+void fs__mktemp(uv_fs_t* req, uv__fs_mktemp_func func) {
+  static const WCHAR *tempchars =
+    L"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
+  static const size_t num_chars = 62;
+  static const size_t num_x = 6;
+  WCHAR *cp, *ep;
+  unsigned int tries, i;
+  size_t len;
+  uint64_t v;
+  char* path;
+  
+  path = (char*)req->path;
+  len = wcslen(req->file.pathw);
+  ep = req->file.pathw + len;
+  if (len < num_x || wcsncmp(ep - num_x, L"XXXXXX", num_x)) {
+    SET_REQ_UV_ERROR(req, UV_EINVAL, ERROR_INVALID_PARAMETER);
+    goto clobber;
+  }
+
+  tries = TMP_MAX;
+  do {
+    if (uv__random_rtlgenrandom((void *)&v, sizeof(v)) < 0) {
+      SET_REQ_UV_ERROR(req, UV_EIO, ERROR_IO_DEVICE);
+      goto clobber;
+    }
+
+    cp = ep - num_x;
+    for (i = 0; i < num_x; i++) {
+      *cp++ = tempchars[v % num_chars];
+      v /= num_chars;
+    }
+
+    if (func(req)) {
+      if (req->result >= 0) {
+        len = strlen(path);
+        wcstombs(path + len - num_x, ep - num_x, num_x);
+      }
+      return;
+    }
+  } while (--tries);
+
+  SET_REQ_WIN32_ERROR(req, GetLastError());
+
+clobber:
+  path[0] = '\0';
+}
+
+
+static int fs__mkdtemp_func(uv_fs_t* req) {
+  DWORD error;
+  if (CreateDirectoryW(req->file.pathw, NULL)) {
+    SET_REQ_RESULT(req, 0);
+    return 1;
+  }
+  error = GetLastError();
+  if (error != ERROR_ALREADY_EXISTS) {
+    SET_REQ_WIN32_ERROR(req, error);
+    return 1;
+  }
+
+  return 0;
+}
+
+
+void fs__mkdtemp(uv_fs_t* req) {
+  fs__mktemp(req, fs__mkdtemp_func);
+}
+
+
+static int fs__mkstemp_func(uv_fs_t* req) {
+  HANDLE file;
+  int fd;
+
+  file = CreateFileW(req->file.pathw,
+                     GENERIC_READ | GENERIC_WRITE,
+                     FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+                     NULL,
+                     CREATE_NEW,
+                     FILE_ATTRIBUTE_NORMAL,
+                     NULL);
+
+  if (file == INVALID_HANDLE_VALUE) {
+    DWORD error;
+    error = GetLastError();
+
+    /* If the file exists, the main fs__mktemp() function
+       will retry. If it's another error, we want to stop. */
+    if (error != ERROR_FILE_EXISTS) {
+      SET_REQ_WIN32_ERROR(req, error);
+      return 1;
+    }
+
+    return 0;
+  }
+
+  fd = _open_osfhandle((intptr_t) file, 0);
+  if (fd < 0) {
+    /* The only known failure mode for _open_osfhandle() is EMFILE, in which
+     * case GetLastError() will return zero. However we'll try to handle other
+     * errors as well, should they ever occur.
+     */
+    if (errno == EMFILE)
+      SET_REQ_UV_ERROR(req, UV_EMFILE, ERROR_TOO_MANY_OPEN_FILES);
+    else if (GetLastError() != ERROR_SUCCESS)
+      SET_REQ_WIN32_ERROR(req, GetLastError());
+    else
+      SET_REQ_WIN32_ERROR(req, UV_UNKNOWN);
+    CloseHandle(file);
+    return 1;
+  }
+
+  SET_REQ_RESULT(req, fd);
+
+  return 1;
+}
+
+
+void fs__mkstemp(uv_fs_t* req) {
+  fs__mktemp(req, fs__mkstemp_func);
+}
+
+
+void fs__scandir(uv_fs_t* req) {
+  static const size_t dirents_initial_size = 32;
+
+  HANDLE dir_handle = INVALID_HANDLE_VALUE;
+
+  uv__dirent_t** dirents = NULL;
+  size_t dirents_size = 0;
+  size_t dirents_used = 0;
+
+  IO_STATUS_BLOCK iosb;
+  NTSTATUS status;
+
+  /* Buffer to hold directory entries returned by NtQueryDirectoryFile.
+   * It's important that this buffer can hold at least one entry, regardless
+   * of the length of the file names present in the enumerated directory.
+   * A file name is at most 256 WCHARs long.
+   * According to MSDN, the buffer must be aligned at an 8-byte boundary.
+   */
+#if _MSC_VER
+  __declspec(align(8)) char buffer[8192];
+#else
+  __attribute__ ((aligned (8))) char buffer[8192];
+#endif
+
+  STATIC_ASSERT(sizeof buffer >=
+                sizeof(FILE_DIRECTORY_INFORMATION) + 256 * sizeof(WCHAR));
+
+  /* Open the directory. */
+  dir_handle =
+      CreateFileW(req->file.pathw,
+                  FILE_LIST_DIRECTORY | SYNCHRONIZE,
+                  FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+                  NULL,
+                  OPEN_EXISTING,
+                  FILE_FLAG_BACKUP_SEMANTICS,
+                  NULL);
+  if (dir_handle == INVALID_HANDLE_VALUE)
+    goto win32_error;
+
+  /* Read the first chunk. */
+  status = pNtQueryDirectoryFile(dir_handle,
+                                 NULL,
+                                 NULL,
+                                 NULL,
+                                 &iosb,
+                                 &buffer,
+                                 sizeof buffer,
+                                 FileDirectoryInformation,
+                                 FALSE,
+                                 NULL,
+                                 TRUE);
+
+  /* If the handle is not a directory, we'll get STATUS_INVALID_PARAMETER.
+   * This should be reported back as UV_ENOTDIR.
+   */
+  if (status == (NTSTATUS)STATUS_INVALID_PARAMETER)
+    goto not_a_directory_error;
+
+  while (NT_SUCCESS(status)) {
+    char* position = buffer;
+    size_t next_entry_offset = 0;
+
+    do {
+      FILE_DIRECTORY_INFORMATION* info;
+      uv__dirent_t* dirent;
+
+      size_t wchar_len;
+      size_t utf8_len;
+
+      /* Obtain a pointer to the current directory entry. */
+      position += next_entry_offset;
+      info = (FILE_DIRECTORY_INFORMATION*) position;
+
+      /* Fetch the offset to the next directory entry. */
+      next_entry_offset = info->NextEntryOffset;
+
+      /* Compute the length of the filename in WCHARs. */
+      wchar_len = info->FileNameLength / sizeof info->FileName[0];
+
+      /* Skip over '.' and '..' entries.  It has been reported that
+       * the SharePoint driver includes the terminating zero byte in
+       * the filename length.  Strip those first.
+       */
+      while (wchar_len > 0 && info->FileName[wchar_len - 1] == L'\0')
+        wchar_len -= 1;
+
+      if (wchar_len == 0)
+        continue;
+      if (wchar_len == 1 && info->FileName[0] == L'.')
+        continue;
+      if (wchar_len == 2 && info->FileName[0] == L'.' &&
+          info->FileName[1] == L'.')
+        continue;
+
+      /* Compute the space required to store the filename as UTF-8. */
+      utf8_len = WideCharToMultiByte(
+          CP_UTF8, 0, &info->FileName[0], wchar_len, NULL, 0, NULL, NULL);
+      if (utf8_len == 0)
+        goto win32_error;
+
+      /* Resize the dirent array if needed. */
+      if (dirents_used >= dirents_size) {
+        size_t new_dirents_size =
+            dirents_size == 0 ? dirents_initial_size : dirents_size << 1;
+        uv__dirent_t** new_dirents = (uv__dirent_t**)
+            uv__realloc(dirents, new_dirents_size * sizeof *dirents);
+
+        if (new_dirents == NULL)
+          goto out_of_memory_error;
+
+        dirents_size = new_dirents_size;
+        dirents = new_dirents;
+      }
+
+      /* Allocate space for the uv dirent structure. The dirent structure
+       * includes room for the first character of the filename, but `utf8_len`
+       * doesn't count the NULL terminator at this point.
+       */
+      dirent = (uv__dirent_t*)uv__malloc(sizeof *dirent + utf8_len);
+      if (dirent == NULL)
+        goto out_of_memory_error;
+
+      dirents[dirents_used++] = dirent;
+
+      /* Convert file name to UTF-8. */
+      if (WideCharToMultiByte(CP_UTF8,
+                              0,
+                              &info->FileName[0],
+                              wchar_len,
+                              &dirent->d_name[0],
+                              utf8_len,
+                              NULL,
+                              NULL) == 0)
+        goto win32_error;
+
+      /* Add a null terminator to the filename. */
+      dirent->d_name[utf8_len] = '\0';
+
+      /* Fill out the type field. */
+      if (info->FileAttributes & FILE_ATTRIBUTE_DEVICE)
+        dirent->d_type = UV__DT_CHAR;
+      else if (info->FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)
+        dirent->d_type = UV__DT_LINK;
+      else if (info->FileAttributes & FILE_ATTRIBUTE_DIRECTORY)
+        dirent->d_type = UV__DT_DIR;
+      else
+        dirent->d_type = UV__DT_FILE;
+    } while (next_entry_offset != 0);
+
+    /* Read the next chunk. */
+    status = pNtQueryDirectoryFile(dir_handle,
+                                   NULL,
+                                   NULL,
+                                   NULL,
+                                   &iosb,
+                                   &buffer,
+                                   sizeof buffer,
+                                   FileDirectoryInformation,
+                                   FALSE,
+                                   NULL,
+                                   FALSE);
+
+    /* After the first pNtQueryDirectoryFile call, the function may return
+     * STATUS_SUCCESS even if the buffer was too small to hold at least one
+     * directory entry.
+     */
+    if (status == STATUS_SUCCESS && iosb.Information == 0)
+      status = STATUS_BUFFER_OVERFLOW;
+  }
+
+  if (status != STATUS_NO_MORE_FILES)
+    goto nt_error;
+
+  CloseHandle(dir_handle);
+
+  /* Store the result in the request object. */
+  req->ptr = dirents;
+  if (dirents != NULL)
+    req->flags |= UV_FS_FREE_PTR;
+
+  SET_REQ_RESULT(req, dirents_used);
+
+  /* `nbufs` will be used as index by uv_fs_scandir_next. */
+  req->fs.info.nbufs = 0;
+
+  return;
+
+nt_error:
+  SET_REQ_WIN32_ERROR(req, pRtlNtStatusToDosError(status));
+  goto cleanup;
+
+win32_error:
+  SET_REQ_WIN32_ERROR(req, GetLastError());
+  goto cleanup;
+
+not_a_directory_error:
+  SET_REQ_UV_ERROR(req, UV_ENOTDIR, ERROR_DIRECTORY);
+  goto cleanup;
+
+out_of_memory_error:
+  SET_REQ_UV_ERROR(req, UV_ENOMEM, ERROR_OUTOFMEMORY);
+  goto cleanup;
+
+cleanup:
+  if (dir_handle != INVALID_HANDLE_VALUE)
+    CloseHandle(dir_handle);
+  while (dirents_used > 0)
+    uv__free(dirents[--dirents_used]);
+  if (dirents != NULL)
+    uv__free(dirents);
+}
+
+void fs__opendir(uv_fs_t* req) {
+  WCHAR* pathw;
+  size_t len;
+  const WCHAR* fmt;
+  WCHAR* find_path;
+  uv_dir_t* dir;
+
+  pathw = req->file.pathw;
+  dir = NULL;
+  find_path = NULL;
+
+  /* Figure out whether path is a file or a directory. */
+  if (!(GetFileAttributesW(pathw) & FILE_ATTRIBUTE_DIRECTORY)) {
+    SET_REQ_UV_ERROR(req, UV_ENOTDIR, ERROR_DIRECTORY);
+    goto error;
+  }
+
+  dir = (uv_dir_t*)uv__malloc(sizeof(*dir));
+  if (dir == NULL) {
+    SET_REQ_UV_ERROR(req, UV_ENOMEM, ERROR_OUTOFMEMORY);
+    goto error;
+  }
+
+  len = wcslen(pathw);
+
+  if (len == 0)
+    fmt = L"./*";
+  else if (IS_SLASH(pathw[len - 1]))
+    fmt = L"%s*";
+  else
+    fmt = L"%s\\*";
+
+  find_path = (WCHAR*)uv__malloc(sizeof(WCHAR) * (len + 4));
+  if (find_path == NULL) {
+    SET_REQ_UV_ERROR(req, UV_ENOMEM, ERROR_OUTOFMEMORY);
+    goto error;
+  }
+
+  _snwprintf(find_path, len + 3, fmt, pathw);
+  dir->dir_handle = FindFirstFileW(find_path, &dir->find_data);
+  uv__free(find_path);
+  find_path = NULL;
+  if (dir->dir_handle == INVALID_HANDLE_VALUE &&
+      GetLastError() != ERROR_FILE_NOT_FOUND) {
+    SET_REQ_WIN32_ERROR(req, GetLastError());
+    goto error;
+  }
+
+  dir->need_find_call = FALSE;
+  req->ptr = dir;
+  SET_REQ_RESULT(req, 0);
+  return;
+
+error:
+  uv__free(dir);
+  uv__free(find_path);
+  req->ptr = NULL;
+}
+
+void fs__readdir(uv_fs_t* req) {
+  uv_dir_t* dir;
+  uv_dirent_t* dirents;
+  uv__dirent_t dent;
+  unsigned int dirent_idx;
+  PWIN32_FIND_DATAW find_data;
+  unsigned int i;
+  int r;
+
+  req->flags |= UV_FS_FREE_PTR;
+  dir = (uv_dir_t*)req->ptr;
+  dirents = dir->dirents;
+  memset(dirents, 0, dir->nentries * sizeof(*dir->dirents));
+  find_data = &dir->find_data;
+  dirent_idx = 0;
+
+  while (dirent_idx < dir->nentries) {
+    if (dir->need_find_call && FindNextFileW(dir->dir_handle, find_data) == 0) {
+      if (GetLastError() == ERROR_NO_MORE_FILES)
+        break;
+      goto error;
+    }
+
+    /* Skip "." and ".." entries. */
+    if (find_data->cFileName[0] == L'.' &&
+        (find_data->cFileName[1] == L'\0' ||
+        (find_data->cFileName[1] == L'.' &&
+        find_data->cFileName[2] == L'\0'))) {
+      dir->need_find_call = TRUE;
+      continue;
+    }
+
+    r = uv__convert_utf16_to_utf8((const WCHAR*) &find_data->cFileName,
+                                  -1,
+                                  (char**) &dirents[dirent_idx].name);
+    if (r != 0)
+      goto error;
+
+    /* Copy file type. */
+    if ((find_data->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0)
+      dent.d_type = UV__DT_DIR;
+    else if ((find_data->dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0)
+      dent.d_type = UV__DT_LINK;
+    else if ((find_data->dwFileAttributes & FILE_ATTRIBUTE_DEVICE) != 0)
+      dent.d_type = UV__DT_CHAR;
+    else
+      dent.d_type = UV__DT_FILE;
+
+    dirents[dirent_idx].type = uv__fs_get_dirent_type(&dent);
+    dir->need_find_call = TRUE;
+    ++dirent_idx;
+  }
+
+  SET_REQ_RESULT(req, dirent_idx);
+  return;
+
+error:
+  SET_REQ_WIN32_ERROR(req, GetLastError());
+  for (i = 0; i < dirent_idx; ++i) {
+    uv__free((char*) dirents[i].name);
+    dirents[i].name = NULL;
+  }
+}
+
+void fs__closedir(uv_fs_t* req) {
+  uv_dir_t* dir;
+
+  dir = (uv_dir_t*)req->ptr;
+  FindClose(dir->dir_handle);
+  uv__free(req->ptr);
+  SET_REQ_RESULT(req, 0);
+}
+
+INLINE static int fs__stat_handle(HANDLE handle, uv_stat_t* statbuf,
+    int do_lstat) {
+  FILE_ALL_INFORMATION file_info;
+  FILE_FS_VOLUME_INFORMATION volume_info;
+  NTSTATUS nt_status;
+  IO_STATUS_BLOCK io_status;
+
+  nt_status = pNtQueryInformationFile(handle,
+                                      &io_status,
+                                      &file_info,
+                                      sizeof file_info,
+                                      FileAllInformation);
+
+  /* Buffer overflow (a warning status code) is expected here. */
+  if (NT_ERROR(nt_status)) {
+    SetLastError(pRtlNtStatusToDosError(nt_status));
+    return -1;
+  }
+
+  nt_status = pNtQueryVolumeInformationFile(handle,
+                                            &io_status,
+                                            &volume_info,
+                                            sizeof volume_info,
+                                            FileFsVolumeInformation);
+
+  /* Buffer overflow (a warning status code) is expected here. */
+  if (io_status.Status == STATUS_NOT_IMPLEMENTED) {
+    statbuf->st_dev = 0;
+  } else if (NT_ERROR(nt_status)) {
+    SetLastError(pRtlNtStatusToDosError(nt_status));
+    return -1;
+  } else {
+    statbuf->st_dev = volume_info.VolumeSerialNumber;
+  }
+
+  /* Todo: st_mode should probably always be 0666 for everyone. We might also
+   * want to report 0777 if the file is a .exe or a directory.
+   *
+   * Currently it's based on whether the 'readonly' attribute is set, which
+   * makes little sense because the semantics are so different: the 'read-only'
+   * flag is just a way for a user to protect against accidental deletion, and
+   * serves no security purpose. Windows uses ACLs for that.
+   *
+   * Also people now use uv_fs_chmod() to take away the writable bit for good
+   * reasons. Windows however just makes the file read-only, which makes it
+   * impossible to delete the file afterwards, since read-only files can't be
+   * deleted.
+   *
+   * IOW it's all just a clusterfuck and we should think of something that
+   * makes slightly more sense.
+   *
+   * And uv_fs_chmod should probably just fail on windows or be a total no-op.
+   * There's nothing sensible it can do anyway.
+   */
+  statbuf->st_mode = 0;
+
+  /*
+  * On Windows, FILE_ATTRIBUTE_REPARSE_POINT is a general purpose mechanism
+  * by which filesystem drivers can intercept and alter file system requests.
+  *
+  * The only reparse points we care about are symlinks and mount points, both
+  * of which are treated as POSIX symlinks. Further, we only care when
+  * invoked via lstat, which seeks information about the link instead of its
+  * target. Otherwise, reparse points must be treated as regular files.
+  */
+  if (do_lstat &&
+      (file_info.BasicInformation.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) {
+    /*
+     * If reading the link fails, the reparse point is not a symlink and needs
+     * to be treated as a regular file. The higher level lstat function will
+     * detect this failure and retry without do_lstat if appropriate.
+     */
+    if (fs__readlink_handle(handle, NULL, &statbuf->st_size) != 0)
+      return -1;
+    statbuf->st_mode |= S_IFLNK;
+  }
+
+  if (statbuf->st_mode == 0) {
+    if (file_info.BasicInformation.FileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
+      statbuf->st_mode |= _S_IFDIR;
+      statbuf->st_size = 0;
+    } else {
+      statbuf->st_mode |= _S_IFREG;
+      statbuf->st_size = file_info.StandardInformation.EndOfFile.QuadPart;
+    }
+  }
+
+  if (file_info.BasicInformation.FileAttributes & FILE_ATTRIBUTE_READONLY)
+    statbuf->st_mode |= _S_IREAD | (_S_IREAD >> 3) | (_S_IREAD >> 6);
+  else
+    statbuf->st_mode |= (_S_IREAD | _S_IWRITE) | ((_S_IREAD | _S_IWRITE) >> 3) |
+                        ((_S_IREAD | _S_IWRITE) >> 6);
+
+  uv__filetime_to_timespec(&statbuf->st_atim,
+                           file_info.BasicInformation.LastAccessTime.QuadPart);
+  uv__filetime_to_timespec(&statbuf->st_ctim,
+                           file_info.BasicInformation.ChangeTime.QuadPart);
+  uv__filetime_to_timespec(&statbuf->st_mtim,
+                           file_info.BasicInformation.LastWriteTime.QuadPart);
+  uv__filetime_to_timespec(&statbuf->st_birthtim,
+                           file_info.BasicInformation.CreationTime.QuadPart);
+
+  statbuf->st_ino = file_info.InternalInformation.IndexNumber.QuadPart;
+
+  /* st_blocks contains the on-disk allocation size in 512-byte units. */
+  statbuf->st_blocks =
+      (uint64_t) file_info.StandardInformation.AllocationSize.QuadPart >> 9;
+
+  statbuf->st_nlink = file_info.StandardInformation.NumberOfLinks;
+
+  /* The st_blksize is supposed to be the 'optimal' number of bytes for reading
+   * and writing to the disk. That is, for any definition of 'optimal' - it's
+   * supposed to at least avoid read-update-write behavior when writing to the
+   * disk.
+   *
+   * However nobody knows this and even fewer people actually use this value,
+   * and in order to fill it out we'd have to make another syscall to query the
+   * volume for FILE_FS_SECTOR_SIZE_INFORMATION.
+   *
+   * Therefore we'll just report a sensible value that's quite commonly okay
+   * on modern hardware.
+   *
+   * 4096 is the minimum required to be compatible with newer Advanced Format
+   * drives (which have 4096 bytes per physical sector), and to be backwards
+   * compatible with older drives (which have 512 bytes per physical sector).
+   */
+  statbuf->st_blksize = 4096;
+
+  /* Todo: set st_flags to something meaningful. Also provide a wrapper for
+   * chattr(2).
+   */
+  statbuf->st_flags = 0;
+
+  /* Windows has nothing sensible to say about these values, so they'll just
+   * remain empty.
+   */
+  statbuf->st_gid = 0;
+  statbuf->st_uid = 0;
+  statbuf->st_rdev = 0;
+  statbuf->st_gen = 0;
+
+  return 0;
+}
+
+
+INLINE static void fs__stat_prepare_path(WCHAR* pathw) {
+  size_t len = wcslen(pathw);
+
+  /* TODO: ignore namespaced paths. */
+  if (len > 1 && pathw[len - 2] != L':' &&
+      (pathw[len - 1] == L'\\' || pathw[len - 1] == L'/')) {
+    pathw[len - 1] = '\0';
+  }
+}
+
+
+INLINE static DWORD fs__stat_impl_from_path(WCHAR* path,
+                                            int do_lstat,
+                                            uv_stat_t* statbuf) {
+  HANDLE handle;
+  DWORD flags;
+  DWORD ret;
+
+  flags = FILE_FLAG_BACKUP_SEMANTICS;
+  if (do_lstat)
+    flags |= FILE_FLAG_OPEN_REPARSE_POINT;
+
+  handle = CreateFileW(path,
+                       FILE_READ_ATTRIBUTES,
+                       FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+                       NULL,
+                       OPEN_EXISTING,
+                       flags,
+                       NULL);
+
+  if (handle == INVALID_HANDLE_VALUE)
+    return GetLastError();
+
+  if (fs__stat_handle(handle, statbuf, do_lstat) != 0)
+    ret = GetLastError();
+  else
+    ret = 0;
+
+  CloseHandle(handle);
+  return ret;
+}
+
+
+INLINE static void fs__stat_impl(uv_fs_t* req, int do_lstat) {
+  DWORD error;
+
+  error = fs__stat_impl_from_path(req->file.pathw, do_lstat, &req->statbuf);
+  if (error != 0) {
+    if (do_lstat &&
+        (error == ERROR_SYMLINK_NOT_SUPPORTED ||
+         error == ERROR_NOT_A_REPARSE_POINT)) {
+      /* We opened a reparse point but it was not a symlink. Try again. */
+      fs__stat_impl(req, 0);
+    } else {
+      /* Stat failed. */
+      SET_REQ_WIN32_ERROR(req, error);
+    }
+
+    return;
+  }
+
+  req->ptr = &req->statbuf;
+  SET_REQ_RESULT(req, 0);
+}
+
+
+static void fs__stat(uv_fs_t* req) {
+  fs__stat_prepare_path(req->file.pathw);
+  fs__stat_impl(req, 0);
+}
+
+
+static void fs__lstat(uv_fs_t* req) {
+  fs__stat_prepare_path(req->file.pathw);
+  fs__stat_impl(req, 1);
+}
+
+
+static void fs__fstat(uv_fs_t* req) {
+  int fd = req->file.fd;
+  HANDLE handle;
+
+  VERIFY_FD(fd, req);
+
+  handle = uv__get_osfhandle(fd);
+
+  if (handle == INVALID_HANDLE_VALUE) {
+    SET_REQ_WIN32_ERROR(req, ERROR_INVALID_HANDLE);
+    return;
+  }
+
+  if (fs__stat_handle(handle, &req->statbuf, 0) != 0) {
+    SET_REQ_WIN32_ERROR(req, GetLastError());
+    return;
+  }
+
+  req->ptr = &req->statbuf;
+  SET_REQ_RESULT(req, 0);
+}
+
+
+static void fs__rename(uv_fs_t* req) {
+  if (!MoveFileExW(req->file.pathw, req->fs.info.new_pathw, MOVEFILE_REPLACE_EXISTING)) {
+    SET_REQ_WIN32_ERROR(req, GetLastError());
+    return;
+  }
+
+  SET_REQ_RESULT(req, 0);
+}
+
+
+INLINE static void fs__sync_impl(uv_fs_t* req) {
+  int fd = req->file.fd;
+  int result;
+
+  VERIFY_FD(fd, req);
+
+  result = FlushFileBuffers(uv__get_osfhandle(fd)) ? 0 : -1;
+  if (result == -1) {
+    SET_REQ_WIN32_ERROR(req, GetLastError());
+  } else {
+    SET_REQ_RESULT(req, result);
+  }
+}
+
+
+static void fs__fsync(uv_fs_t* req) {
+  fs__sync_impl(req);
+}
+
+
+static void fs__fdatasync(uv_fs_t* req) {
+  fs__sync_impl(req);
+}
+
+
+static void fs__ftruncate(uv_fs_t* req) {
+  int fd = req->file.fd;
+  HANDLE handle;
+  struct uv__fd_info_s fd_info = { 0 };
+  NTSTATUS status;
+  IO_STATUS_BLOCK io_status;
+  FILE_END_OF_FILE_INFORMATION eof_info;
+
+  VERIFY_FD(fd, req);
+
+  handle = uv__get_osfhandle(fd);
+
+  if (uv__fd_hash_get(fd, &fd_info)) {
+    if (fd_info.is_directory) {
+      SET_REQ_WIN32_ERROR(req, ERROR_ACCESS_DENIED);
+      return;
+    }
+
+    if (fd_info.mapping != INVALID_HANDLE_VALUE) {
+      CloseHandle(fd_info.mapping);
+    }
+  }
+
+  eof_info.EndOfFile.QuadPart = req->fs.info.offset;
+
+  status = pNtSetInformationFile(handle,
+                                 &io_status,
+                                 &eof_info,
+                                 sizeof eof_info,
+                                 FileEndOfFileInformation);
+
+  if (NT_SUCCESS(status)) {
+    SET_REQ_RESULT(req, 0);
+  } else {
+    SET_REQ_WIN32_ERROR(req, pRtlNtStatusToDosError(status));
+
+    if (fd_info.flags) {
+      CloseHandle(handle);
+      fd_info.mapping = INVALID_HANDLE_VALUE;
+      fd_info.size.QuadPart = 0;
+      fd_info.current_pos.QuadPart = 0;
+      uv__fd_hash_add(fd, &fd_info);
+      return;
+    }
+  }
+
+  if (fd_info.flags) {
+    fd_info.size = eof_info.EndOfFile;
+
+    if (fd_info.size.QuadPart == 0) {
+      fd_info.mapping = INVALID_HANDLE_VALUE;
+    } else {
+      DWORD flProtect = (fd_info.flags & (UV_FS_O_RDONLY | UV_FS_O_WRONLY |
+        UV_FS_O_RDWR)) == UV_FS_O_RDONLY ? PAGE_READONLY : PAGE_READWRITE;
+      fd_info.mapping = CreateFileMapping(handle,
+                                          NULL,
+                                          flProtect,
+                                          fd_info.size.HighPart,
+                                          fd_info.size.LowPart,
+                                          NULL);
+      if (fd_info.mapping == NULL) {
+        SET_REQ_WIN32_ERROR(req, GetLastError());
+        CloseHandle(handle);
+        fd_info.mapping = INVALID_HANDLE_VALUE;
+        fd_info.size.QuadPart = 0;
+        fd_info.current_pos.QuadPart = 0;
+        uv__fd_hash_add(fd, &fd_info);
+        return;
+      }
+    }
+
+    uv__fd_hash_add(fd, &fd_info);
+  }
+}
+
+
+static void fs__copyfile(uv_fs_t* req) {
+  int flags;
+  int overwrite;
+  uv_stat_t statbuf;
+  uv_stat_t new_statbuf;
+
+  flags = req->fs.info.file_flags;
+
+  if (flags & UV_FS_COPYFILE_FICLONE_FORCE) {
+    SET_REQ_UV_ERROR(req, UV_ENOSYS, ERROR_NOT_SUPPORTED);
+    return;
+  }
+
+  overwrite = flags & UV_FS_COPYFILE_EXCL;
+
+  if (CopyFileW(req->file.pathw, req->fs.info.new_pathw, overwrite) != 0) {
+    SET_REQ_RESULT(req, 0);
+    return;
+  }
+
+  SET_REQ_WIN32_ERROR(req, GetLastError());
+  if (req->result != UV_EBUSY)
+    return;
+
+  /* if error UV_EBUSY check if src and dst file are the same */
+  if (fs__stat_impl_from_path(req->file.pathw, 0, &statbuf) != 0 ||
+      fs__stat_impl_from_path(req->fs.info.new_pathw, 0, &new_statbuf) != 0) {
+    return;
+  }
+
+  if (statbuf.st_dev == new_statbuf.st_dev &&
+      statbuf.st_ino == new_statbuf.st_ino) {
+    SET_REQ_RESULT(req, 0);
+  }
+}
+
+
+static void fs__sendfile(uv_fs_t* req) {
+  int fd_in = req->file.fd, fd_out = req->fs.info.fd_out;
+  size_t length = req->fs.info.bufsml[0].len;
+  int64_t offset = req->fs.info.offset;
+  const size_t max_buf_size = 65536;
+  size_t buf_size = length < max_buf_size ? length : max_buf_size;
+  int n, result = 0;
+  int64_t result_offset = 0;
+  char* buf = (char*) uv__malloc(buf_size);
+  if (!buf) {
+    uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc");
+  }
+
+  if (offset != -1) {
+    result_offset = _lseeki64(fd_in, offset, SEEK_SET);
+  }
+
+  if (result_offset == -1) {
+    result = -1;
+  } else {
+    while (length > 0) {
+      n = _read(fd_in, buf, length < buf_size ? length : buf_size);
+      if (n == 0) {
+        break;
+      } else if (n == -1) {
+        result = -1;
+        break;
+      }
+
+      length -= n;
+
+      n = _write(fd_out, buf, n);
+      if (n == -1) {
+        result = -1;
+        break;
+      }
+
+      result += n;
+    }
+  }
+
+  uv__free(buf);
+
+  SET_REQ_RESULT(req, result);
+}
+
+
+static void fs__access(uv_fs_t* req) {
+  DWORD attr = GetFileAttributesW(req->file.pathw);
+
+  if (attr == INVALID_FILE_ATTRIBUTES) {
+    SET_REQ_WIN32_ERROR(req, GetLastError());
+    return;
+  }
+
+  /*
+   * Access is possible if
+   * - write access wasn't requested,
+   * - or the file isn't read-only,
+   * - or it's a directory.
+   * (Directories cannot be read-only on Windows.)
+   */
+  if (!(req->fs.info.mode & W_OK) ||
+      !(attr & FILE_ATTRIBUTE_READONLY) ||
+      (attr & FILE_ATTRIBUTE_DIRECTORY)) {
+    SET_REQ_RESULT(req, 0);
+  } else {
+    SET_REQ_WIN32_ERROR(req, UV_EPERM);
+  }
+
+}
+
+
+static void fs__chmod(uv_fs_t* req) {
+  int result = _wchmod(req->file.pathw, req->fs.info.mode);
+  if (result == -1)
+    SET_REQ_WIN32_ERROR(req, _doserrno);
+  else
+    SET_REQ_RESULT(req, 0);
+}
+
+
+static void fs__fchmod(uv_fs_t* req) {
+  int fd = req->file.fd;
+  int clear_archive_flag;
+  HANDLE handle;
+  NTSTATUS nt_status;
+  IO_STATUS_BLOCK io_status;
+  FILE_BASIC_INFORMATION file_info;
+
+  VERIFY_FD(fd, req);
+
+  handle = ReOpenFile(uv__get_osfhandle(fd), FILE_WRITE_ATTRIBUTES, 0, 0);
+  if (handle == INVALID_HANDLE_VALUE) {
+    SET_REQ_WIN32_ERROR(req, GetLastError());
+    return;
+  }
+
+  nt_status = pNtQueryInformationFile(handle,
+                                      &io_status,
+                                      &file_info,
+                                      sizeof file_info,
+                                      FileBasicInformation);
+
+  if (!NT_SUCCESS(nt_status)) {
+    SET_REQ_WIN32_ERROR(req, pRtlNtStatusToDosError(nt_status));
+    goto fchmod_cleanup;
+  }
+
+  /* Test if the Archive attribute is cleared */
+  if ((file_info.FileAttributes & FILE_ATTRIBUTE_ARCHIVE) == 0) {
+      /* Set Archive flag, otherwise setting or clearing the read-only
+         flag will not work */
+      file_info.FileAttributes |= FILE_ATTRIBUTE_ARCHIVE;
+      nt_status = pNtSetInformationFile(handle,
+                                        &io_status,
+                                        &file_info,
+                                        sizeof file_info,
+                                        FileBasicInformation);
+      if (!NT_SUCCESS(nt_status)) {
+        SET_REQ_WIN32_ERROR(req, pRtlNtStatusToDosError(nt_status));
+        goto fchmod_cleanup;
+      }
+      /* Remeber to clear the flag later on */
+      clear_archive_flag = 1;
+  } else {
+      clear_archive_flag = 0;
+  }
+
+  if (req->fs.info.mode & _S_IWRITE) {
+    file_info.FileAttributes &= ~FILE_ATTRIBUTE_READONLY;
+  } else {
+    file_info.FileAttributes |= FILE_ATTRIBUTE_READONLY;
+  }
+
+  nt_status = pNtSetInformationFile(handle,
+                                    &io_status,
+                                    &file_info,
+                                    sizeof file_info,
+                                    FileBasicInformation);
+
+  if (!NT_SUCCESS(nt_status)) {
+    SET_REQ_WIN32_ERROR(req, pRtlNtStatusToDosError(nt_status));
+    goto fchmod_cleanup;
+  }
+
+  if (clear_archive_flag) {
+      file_info.FileAttributes &= ~FILE_ATTRIBUTE_ARCHIVE;
+      if (file_info.FileAttributes == 0) {
+          file_info.FileAttributes = FILE_ATTRIBUTE_NORMAL;
+      }
+      nt_status = pNtSetInformationFile(handle,
+                                        &io_status,
+                                        &file_info,
+                                        sizeof file_info,
+                                        FileBasicInformation);
+      if (!NT_SUCCESS(nt_status)) {
+        SET_REQ_WIN32_ERROR(req, pRtlNtStatusToDosError(nt_status));
+        goto fchmod_cleanup;
+      }
+  }
+
+  SET_REQ_SUCCESS(req);
+fchmod_cleanup:
+  CloseHandle(handle);
+}
+
+
+INLINE static int fs__utime_handle(HANDLE handle, double atime, double mtime) {
+  FILETIME filetime_a, filetime_m;
+
+  TIME_T_TO_FILETIME(atime, &filetime_a);
+  TIME_T_TO_FILETIME(mtime, &filetime_m);
+
+  if (!SetFileTime(handle, NULL, &filetime_a, &filetime_m)) {
+    return -1;
+  }
+
+  return 0;
+}
+
+INLINE static DWORD fs__utime_impl_from_path(WCHAR* path,
+                                             double atime,
+                                             double mtime,
+                                             int do_lutime) {
+  HANDLE handle;
+  DWORD flags;
+  DWORD ret;
+
+  flags = FILE_FLAG_BACKUP_SEMANTICS;
+  if (do_lutime) {
+    flags |= FILE_FLAG_OPEN_REPARSE_POINT;
+  }
+
+  handle = CreateFileW(path,
+                       FILE_WRITE_ATTRIBUTES,
+                       FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+                       NULL,
+                       OPEN_EXISTING,
+                       flags,
+                       NULL);
+
+  if (handle == INVALID_HANDLE_VALUE)
+    return GetLastError();
+
+  if (fs__utime_handle(handle, atime, mtime) != 0)
+    ret = GetLastError();
+  else
+    ret = 0;
+
+  CloseHandle(handle);
+  return ret;
+}
+
+INLINE static void fs__utime_impl(uv_fs_t* req, int do_lutime) {
+  DWORD error;
+
+  error = fs__utime_impl_from_path(req->file.pathw,
+                                   req->fs.time.atime,
+                                   req->fs.time.mtime,
+                                   do_lutime);
+
+  if (error != 0) {
+    if (do_lutime &&
+        (error == ERROR_SYMLINK_NOT_SUPPORTED ||
+         error == ERROR_NOT_A_REPARSE_POINT)) {
+      /* Opened file is a reparse point but not a symlink. Try again. */
+      fs__utime_impl(req, 0);
+    } else {
+      /* utime failed. */
+      SET_REQ_WIN32_ERROR(req, error);
+    }
+
+    return;
+  }
+
+  SET_REQ_RESULT(req, 0);
+}
+
+static void fs__utime(uv_fs_t* req) {
+  fs__utime_impl(req, /* do_lutime */ 0);
+}
+
+
+static void fs__futime(uv_fs_t* req) {
+  int fd = req->file.fd;
+  HANDLE handle;
+  VERIFY_FD(fd, req);
+
+  handle = uv__get_osfhandle(fd);
+
+  if (handle == INVALID_HANDLE_VALUE) {
+    SET_REQ_WIN32_ERROR(req, ERROR_INVALID_HANDLE);
+    return;
+  }
+
+  if (fs__utime_handle(handle, req->fs.time.atime, req->fs.time.mtime) != 0) {
+    SET_REQ_WIN32_ERROR(req, GetLastError());
+    return;
+  }
+
+  SET_REQ_RESULT(req, 0);
+}
+
+static void fs__lutime(uv_fs_t* req) {
+  fs__utime_impl(req, /* do_lutime */ 1);
+}
+
+
+static void fs__link(uv_fs_t* req) {
+  DWORD r = CreateHardLinkW(req->fs.info.new_pathw, req->file.pathw, NULL);
+  if (r == 0)
+    SET_REQ_WIN32_ERROR(req, GetLastError());
+  else
+    SET_REQ_RESULT(req, 0);
+}
+
+
+static void fs__create_junction(uv_fs_t* req, const WCHAR* path,
+    const WCHAR* new_path) {
+  HANDLE handle = INVALID_HANDLE_VALUE;
+  REPARSE_DATA_BUFFER *buffer = NULL;
+  int created = 0;
+  int target_len;
+  int is_absolute, is_long_path;
+  int needed_buf_size, used_buf_size, used_data_size, path_buf_len;
+  int start, len, i;
+  int add_slash;
+  DWORD bytes;
+  WCHAR* path_buf;
+
+  target_len = wcslen(path);
+  is_long_path = wcsncmp(path, LONG_PATH_PREFIX, LONG_PATH_PREFIX_LEN) == 0;
+
+  if (is_long_path) {
+    is_absolute = 1;
+  } else {
+    is_absolute = target_len >= 3 && IS_LETTER(path[0]) &&
+      path[1] == L':' && IS_SLASH(path[2]);
+  }
+
+  if (!is_absolute) {
+    /* Not supporting relative paths */
+    SET_REQ_UV_ERROR(req, UV_EINVAL, ERROR_NOT_SUPPORTED);
+    return;
+  }
+
+  /* Do a pessimistic calculation of the required buffer size */
+  needed_buf_size =
+      FIELD_OFFSET(REPARSE_DATA_BUFFER, MountPointReparseBuffer.PathBuffer) +
+      JUNCTION_PREFIX_LEN * sizeof(WCHAR) +
+      2 * (target_len + 2) * sizeof(WCHAR);
+
+  /* Allocate the buffer */
+  buffer = (REPARSE_DATA_BUFFER*)uv__malloc(needed_buf_size);
+  if (!buffer) {
+    uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc");
+  }
+
+  /* Grab a pointer to the part of the buffer where filenames go */
+  path_buf = (WCHAR*)&(buffer->MountPointReparseBuffer.PathBuffer);
+  path_buf_len = 0;
+
+  /* Copy the substitute (internal) target path */
+  start = path_buf_len;
+
+  wcsncpy((WCHAR*)&path_buf[path_buf_len], JUNCTION_PREFIX,
+    JUNCTION_PREFIX_LEN);
+  path_buf_len += JUNCTION_PREFIX_LEN;
+
+  add_slash = 0;
+  for (i = is_long_path ? LONG_PATH_PREFIX_LEN : 0; path[i] != L'\0'; i++) {
+    if (IS_SLASH(path[i])) {
+      add_slash = 1;
+      continue;
+    }
+
+    if (add_slash) {
+      path_buf[path_buf_len++] = L'\\';
+      add_slash = 0;
+    }
+
+    path_buf[path_buf_len++] = path[i];
+  }
+  path_buf[path_buf_len++] = L'\\';
+  len = path_buf_len - start;
+
+  /* Set the info about the substitute name */
+  buffer->MountPointReparseBuffer.SubstituteNameOffset = start * sizeof(WCHAR);
+  buffer->MountPointReparseBuffer.SubstituteNameLength = len * sizeof(WCHAR);
+
+  /* Insert null terminator */
+  path_buf[path_buf_len++] = L'\0';
+
+  /* Copy the print name of the target path */
+  start = path_buf_len;
+  add_slash = 0;
+  for (i = is_long_path ? LONG_PATH_PREFIX_LEN : 0; path[i] != L'\0'; i++) {
+    if (IS_SLASH(path[i])) {
+      add_slash = 1;
+      continue;
+    }
+
+    if (add_slash) {
+      path_buf[path_buf_len++] = L'\\';
+      add_slash = 0;
+    }
+
+    path_buf[path_buf_len++] = path[i];
+  }
+  len = path_buf_len - start;
+  if (len == 2) {
+    path_buf[path_buf_len++] = L'\\';
+    len++;
+  }
+
+  /* Set the info about the print name */
+  buffer->MountPointReparseBuffer.PrintNameOffset = start * sizeof(WCHAR);
+  buffer->MountPointReparseBuffer.PrintNameLength = len * sizeof(WCHAR);
+
+  /* Insert another null terminator */
+  path_buf[path_buf_len++] = L'\0';
+
+  /* Calculate how much buffer space was actually used */
+  used_buf_size = FIELD_OFFSET(REPARSE_DATA_BUFFER, MountPointReparseBuffer.PathBuffer) +
+    path_buf_len * sizeof(WCHAR);
+  used_data_size = used_buf_size -
+    FIELD_OFFSET(REPARSE_DATA_BUFFER, MountPointReparseBuffer);
+
+  /* Put general info in the data buffer */
+  buffer->ReparseTag = IO_REPARSE_TAG_MOUNT_POINT;
+  buffer->ReparseDataLength = used_data_size;
+  buffer->Reserved = 0;
+
+  /* Create a new directory */
+  if (!CreateDirectoryW(new_path, NULL)) {
+    SET_REQ_WIN32_ERROR(req, GetLastError());
+    goto error;
+  }
+  created = 1;
+
+  /* Open the directory */
+  handle = CreateFileW(new_path,
+                       GENERIC_WRITE,
+                       0,
+                       NULL,
+                       OPEN_EXISTING,
+                       FILE_FLAG_BACKUP_SEMANTICS |
+                         FILE_FLAG_OPEN_REPARSE_POINT,
+                       NULL);
+  if (handle == INVALID_HANDLE_VALUE) {
+    SET_REQ_WIN32_ERROR(req, GetLastError());
+    goto error;
+  }
+
+  /* Create the actual reparse point */
+  if (!DeviceIoControl(handle,
+                       FSCTL_SET_REPARSE_POINT,
+                       buffer,
+                       used_buf_size,
+                       NULL,
+                       0,
+                       &bytes,
+                       NULL)) {
+    SET_REQ_WIN32_ERROR(req, GetLastError());
+    goto error;
+  }
+
+  /* Clean up */
+  CloseHandle(handle);
+  uv__free(buffer);
+
+  SET_REQ_RESULT(req, 0);
+  return;
+
+error:
+  uv__free(buffer);
+
+  if (handle != INVALID_HANDLE_VALUE) {
+    CloseHandle(handle);
+  }
+
+  if (created) {
+    RemoveDirectoryW(new_path);
+  }
+}
+
+
+static void fs__symlink(uv_fs_t* req) {
+  WCHAR* pathw;
+  WCHAR* new_pathw;
+  int flags;
+  int err;
+
+  pathw = req->file.pathw;
+  new_pathw = req->fs.info.new_pathw;
+
+  if (req->fs.info.file_flags & UV_FS_SYMLINK_JUNCTION) {
+    fs__create_junction(req, pathw, new_pathw);
+    return;
+  }
+
+  if (req->fs.info.file_flags & UV_FS_SYMLINK_DIR)
+    flags = SYMBOLIC_LINK_FLAG_DIRECTORY | uv__file_symlink_usermode_flag;
+  else
+    flags = uv__file_symlink_usermode_flag;
+
+  if (CreateSymbolicLinkW(new_pathw, pathw, flags)) {
+    SET_REQ_RESULT(req, 0);
+    return;
+  }
+
+  /* Something went wrong. We will test if it is because of user-mode
+   * symlinks.
+   */
+  err = GetLastError();
+  if (err == ERROR_INVALID_PARAMETER &&
+      flags & SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE) {
+    /* This system does not support user-mode symlinks. We will clear the
+     * unsupported flag and retry.
+     */
+    uv__file_symlink_usermode_flag = 0;
+    fs__symlink(req);
+  } else {
+    SET_REQ_WIN32_ERROR(req, err);
+  }
+}
+
+
+static void fs__readlink(uv_fs_t* req) {
+  HANDLE handle;
+
+  handle = CreateFileW(req->file.pathw,
+                       0,
+                       0,
+                       NULL,
+                       OPEN_EXISTING,
+                       FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS,
+                       NULL);
+
+  if (handle == INVALID_HANDLE_VALUE) {
+    SET_REQ_WIN32_ERROR(req, GetLastError());
+    return;
+  }
+
+  if (fs__readlink_handle(handle, (char**) &req->ptr, NULL) != 0) {
+    SET_REQ_WIN32_ERROR(req, GetLastError());
+    CloseHandle(handle);
+    return;
+  }
+
+  req->flags |= UV_FS_FREE_PTR;
+  SET_REQ_RESULT(req, 0);
+
+  CloseHandle(handle);
+}
+
+
+static ssize_t fs__realpath_handle(HANDLE handle, char** realpath_ptr) {
+  int r;
+  DWORD w_realpath_len;
+  WCHAR* w_realpath_ptr = NULL;
+  WCHAR* w_realpath_buf;
+
+  w_realpath_len = GetFinalPathNameByHandleW(handle, NULL, 0, VOLUME_NAME_DOS);
+  if (w_realpath_len == 0) {
+    return -1;
+  }
+
+  w_realpath_buf = (WCHAR*)uv__malloc((w_realpath_len + 1) * sizeof(WCHAR));
+  if (w_realpath_buf == NULL) {
+    SetLastError(ERROR_OUTOFMEMORY);
+    return -1;
+  }
+  w_realpath_ptr = w_realpath_buf;
+
+  if (GetFinalPathNameByHandleW(
+          handle, w_realpath_ptr, w_realpath_len, VOLUME_NAME_DOS) == 0) {
+    uv__free(w_realpath_buf);
+    SetLastError(ERROR_INVALID_HANDLE);
+    return -1;
+  }
+
+  /* convert UNC path to long path */
+  if (wcsncmp(w_realpath_ptr,
+              UNC_PATH_PREFIX,
+              UNC_PATH_PREFIX_LEN) == 0) {
+    w_realpath_ptr += 6;
+    *w_realpath_ptr = L'\\';
+    w_realpath_len -= 6;
+  } else if (wcsncmp(w_realpath_ptr,
+                      LONG_PATH_PREFIX,
+                      LONG_PATH_PREFIX_LEN) == 0) {
+    w_realpath_ptr += 4;
+    w_realpath_len -= 4;
+  } else {
+    uv__free(w_realpath_buf);
+    SetLastError(ERROR_INVALID_HANDLE);
+    return -1;
+  }
+
+  r = fs__wide_to_utf8(w_realpath_ptr, w_realpath_len, realpath_ptr, NULL);
+  uv__free(w_realpath_buf);
+  return r;
+}
+
+static void fs__realpath(uv_fs_t* req) {
+  HANDLE handle;
+
+  handle = CreateFileW(req->file.pathw,
+                       0,
+                       0,
+                       NULL,
+                       OPEN_EXISTING,
+                       FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS,
+                       NULL);
+  if (handle == INVALID_HANDLE_VALUE) {
+    SET_REQ_WIN32_ERROR(req, GetLastError());
+    return;
+  }
+
+  if (fs__realpath_handle(handle, (char**) &req->ptr) == -1) {
+    CloseHandle(handle);
+    SET_REQ_WIN32_ERROR(req, GetLastError());
+    return;
+  }
+
+  CloseHandle(handle);
+  req->flags |= UV_FS_FREE_PTR;
+  SET_REQ_RESULT(req, 0);
+}
+
+
+static void fs__chown(uv_fs_t* req) {
+  SET_REQ_RESULT(req, 0);
+}
+
+
+static void fs__fchown(uv_fs_t* req) {
+  SET_REQ_RESULT(req, 0);
+}
+
+
+static void fs__lchown(uv_fs_t* req) {
+  SET_REQ_RESULT(req, 0);
+}
+
+
+static void fs__statfs(uv_fs_t* req) {
+  uv_statfs_t* stat_fs;
+  DWORD sectors_per_cluster;
+  DWORD bytes_per_sector;
+  DWORD free_clusters;
+  DWORD total_clusters;
+  WCHAR* pathw;
+
+  pathw = req->file.pathw;
+retry_get_disk_free_space:
+  if (0 == GetDiskFreeSpaceW(pathw,
+                             &sectors_per_cluster,
+                             &bytes_per_sector,
+                             &free_clusters,
+                             &total_clusters)) {
+    DWORD err;
+    WCHAR* fpart;
+    size_t len;
+    DWORD ret;
+    BOOL is_second;
+
+    err = GetLastError();
+    is_second = pathw != req->file.pathw;
+    if (err != ERROR_DIRECTORY || is_second) {
+      if (is_second)
+        uv__free(pathw);
+
+      SET_REQ_WIN32_ERROR(req, err);
+      return;
+    }
+
+    len = MAX_PATH + 1;
+    pathw = (WCHAR*)uv__malloc(len * sizeof(*pathw));
+    if (pathw == NULL) {
+      SET_REQ_UV_ERROR(req, UV_ENOMEM, ERROR_OUTOFMEMORY);
+      return;
+    }
+retry_get_full_path_name:
+    ret = GetFullPathNameW(req->file.pathw,
+                           len,
+                           pathw,
+                           &fpart);
+    if (ret == 0) {
+      uv__free(pathw);
+      SET_REQ_WIN32_ERROR(req, err);
+      return;
+    } else if (ret > len) {
+      len = ret;
+      pathw = (WCHAR*)uv__reallocf(pathw, len * sizeof(*pathw));
+      if (pathw == NULL) {
+        SET_REQ_UV_ERROR(req, UV_ENOMEM, ERROR_OUTOFMEMORY);
+        return;
+      }
+      goto retry_get_full_path_name;
+    }
+    if (fpart != 0)
+      *fpart = L'\0';
+
+    goto retry_get_disk_free_space;
+  }
+  if (pathw != req->file.pathw) {
+    uv__free(pathw);
+  }
+
+  stat_fs = (uv_statfs_t*)uv__malloc(sizeof(*stat_fs));
+  if (stat_fs == NULL) {
+    SET_REQ_UV_ERROR(req, UV_ENOMEM, ERROR_OUTOFMEMORY);
+    return;
+  }
+
+  stat_fs->f_type = 0;
+  stat_fs->f_bsize = bytes_per_sector * sectors_per_cluster;
+  stat_fs->f_blocks = total_clusters;
+  stat_fs->f_bfree = free_clusters;
+  stat_fs->f_bavail = free_clusters;
+  stat_fs->f_files = 0;
+  stat_fs->f_ffree = 0;
+  req->ptr = stat_fs;
+  req->flags |= UV_FS_FREE_PTR;
+  SET_REQ_RESULT(req, 0);
+}
+
+
+static void uv__fs_work(struct uv__work* w) {
+  uv_fs_t* req;
+
+  req = container_of(w, uv_fs_t, work_req);
+  assert(req->type == UV_FS);
+
+#define XX(uc, lc)  case UV_FS_##uc: fs__##lc(req); break;
+  switch (req->fs_type) {
+    XX(OPEN, open)
+    XX(CLOSE, close)
+    XX(READ, read)
+    XX(WRITE, write)
+    XX(COPYFILE, copyfile)
+    XX(SENDFILE, sendfile)
+    XX(STAT, stat)
+    XX(LSTAT, lstat)
+    XX(FSTAT, fstat)
+    XX(FTRUNCATE, ftruncate)
+    XX(UTIME, utime)
+    XX(FUTIME, futime)
+    XX(LUTIME, lutime)
+    XX(ACCESS, access)
+    XX(CHMOD, chmod)
+    XX(FCHMOD, fchmod)
+    XX(FSYNC, fsync)
+    XX(FDATASYNC, fdatasync)
+    XX(UNLINK, unlink)
+    XX(RMDIR, rmdir)
+    XX(MKDIR, mkdir)
+    XX(MKDTEMP, mkdtemp)
+    XX(MKSTEMP, mkstemp)
+    XX(RENAME, rename)
+    XX(SCANDIR, scandir)
+    XX(READDIR, readdir)
+    XX(OPENDIR, opendir)
+    XX(CLOSEDIR, closedir)
+    XX(LINK, link)
+    XX(SYMLINK, symlink)
+    XX(READLINK, readlink)
+    XX(REALPATH, realpath)
+    XX(CHOWN, chown)
+    XX(FCHOWN, fchown)
+    XX(LCHOWN, lchown)
+    XX(STATFS, statfs)
+    default:
+      assert(!"bad uv_fs_type");
+  }
+}
+
+
+static void uv__fs_done(struct uv__work* w, int status) {
+  uv_fs_t* req;
+
+  req = container_of(w, uv_fs_t, work_req);
+  uv__req_unregister(req->loop, req);
+
+  if (status == UV_ECANCELED) {
+    assert(req->result == 0);
+    SET_REQ_UV_ERROR(req, UV_ECANCELED, 0);
+  }
+
+  req->cb(req);
+}
+
+
+void uv_fs_req_cleanup(uv_fs_t* req) {
+  if (req == NULL)
+    return;
+
+  if (req->flags & UV_FS_CLEANEDUP)
+    return;
+
+  if (req->flags & UV_FS_FREE_PATHS)
+    uv__free(req->file.pathw);
+
+  if (req->flags & UV_FS_FREE_PTR) {
+    if (req->fs_type == UV_FS_SCANDIR && req->ptr != NULL)
+      uv__fs_scandir_cleanup(req);
+    else if (req->fs_type == UV_FS_READDIR)
+      uv__fs_readdir_cleanup(req);
+    else
+      uv__free(req->ptr);
+  }
+
+  if (req->fs.info.bufs != req->fs.info.bufsml)
+    uv__free(req->fs.info.bufs);
+
+  req->path = NULL;
+  req->file.pathw = NULL;
+  req->fs.info.new_pathw = NULL;
+  req->fs.info.bufs = NULL;
+  req->ptr = NULL;
+
+  req->flags |= UV_FS_CLEANEDUP;
+}
+
+
+int uv_fs_open(uv_loop_t* loop, uv_fs_t* req, const char* path, int flags,
+    int mode, uv_fs_cb cb) {
+  int err;
+
+  INIT(UV_FS_OPEN);
+  err = fs__capture_path(req, path, NULL, cb != NULL);
+  if (err) {
+    SET_REQ_WIN32_ERROR(req, err);
+    return req->result;
+  }
+
+  req->fs.info.file_flags = flags;
+  req->fs.info.mode = mode;
+  POST;
+}
+
+
+int uv_fs_close(uv_loop_t* loop, uv_fs_t* req, uv_file fd, uv_fs_cb cb) {
+  INIT(UV_FS_CLOSE);
+  req->file.fd = fd;
+  POST;
+}
+
+
+int uv_fs_read(uv_loop_t* loop,
+               uv_fs_t* req,
+               uv_file fd,
+               const uv_buf_t bufs[],
+               unsigned int nbufs,
+               int64_t offset,
+               uv_fs_cb cb) {
+  INIT(UV_FS_READ);
+
+  if (bufs == NULL || nbufs == 0) {
+    SET_REQ_UV_ERROR(req, UV_EINVAL, ERROR_INVALID_PARAMETER);
+    return UV_EINVAL;
+  }
+
+  req->file.fd = fd;
+
+  req->fs.info.nbufs = nbufs;
+  req->fs.info.bufs = req->fs.info.bufsml;
+  if (nbufs > ARRAY_SIZE(req->fs.info.bufsml))
+    req->fs.info.bufs = (uv_buf_t*)uv__malloc(nbufs * sizeof(*bufs));
+
+  if (req->fs.info.bufs == NULL) {
+    SET_REQ_UV_ERROR(req, UV_ENOMEM, ERROR_OUTOFMEMORY);
+    return UV_ENOMEM;
+  }
+
+  memcpy(req->fs.info.bufs, bufs, nbufs * sizeof(*bufs));
+
+  req->fs.info.offset = offset;
+  POST;
+}
+
+
+int uv_fs_write(uv_loop_t* loop,
+                uv_fs_t* req,
+                uv_file fd,
+                const uv_buf_t bufs[],
+                unsigned int nbufs,
+                int64_t offset,
+                uv_fs_cb cb) {
+  INIT(UV_FS_WRITE);
+
+  if (bufs == NULL || nbufs == 0) {
+    SET_REQ_UV_ERROR(req, UV_EINVAL, ERROR_INVALID_PARAMETER);
+    return UV_EINVAL;
+  }
+
+  req->file.fd = fd;
+
+  req->fs.info.nbufs = nbufs;
+  req->fs.info.bufs = req->fs.info.bufsml;
+  if (nbufs > ARRAY_SIZE(req->fs.info.bufsml))
+    req->fs.info.bufs = (uv_buf_t*)uv__malloc(nbufs * sizeof(*bufs));
+
+  if (req->fs.info.bufs == NULL) {
+    SET_REQ_UV_ERROR(req, UV_ENOMEM, ERROR_OUTOFMEMORY);
+    return UV_ENOMEM;
+  }
+
+  memcpy(req->fs.info.bufs, bufs, nbufs * sizeof(*bufs));
+
+  req->fs.info.offset = offset;
+  POST;
+}
+
+
+int uv_fs_unlink(uv_loop_t* loop, uv_fs_t* req, const char* path,
+    uv_fs_cb cb) {
+  int err;
+
+  INIT(UV_FS_UNLINK);
+  err = fs__capture_path(req, path, NULL, cb != NULL);
+  if (err) {
+    SET_REQ_WIN32_ERROR(req, err);
+    return req->result;
+  }
+
+  POST;
+}
+
+
+int uv_fs_mkdir(uv_loop_t* loop, uv_fs_t* req, const char* path, int mode,
+    uv_fs_cb cb) {
+  int err;
+
+  INIT(UV_FS_MKDIR);
+  err = fs__capture_path(req, path, NULL, cb != NULL);
+  if (err) {
+    SET_REQ_WIN32_ERROR(req, err);
+    return req->result;
+  }
+
+  req->fs.info.mode = mode;
+  POST;
+}
+
+
+int uv_fs_mkdtemp(uv_loop_t* loop,
+                  uv_fs_t* req,
+                  const char* tpl,
+                  uv_fs_cb cb) {
+  int err;
+
+  INIT(UV_FS_MKDTEMP);
+  err = fs__capture_path(req, tpl, NULL, TRUE);
+  if (err) {
+    SET_REQ_WIN32_ERROR(req, err);
+    return req->result;
+  }
+
+  POST;
+}
+
+
+int uv_fs_mkstemp(uv_loop_t* loop,
+                  uv_fs_t* req,
+                  const char* tpl,
+                  uv_fs_cb cb) {
+  int err;
+
+  INIT(UV_FS_MKSTEMP);
+  err = fs__capture_path(req, tpl, NULL, TRUE);
+  if (err) {
+    SET_REQ_WIN32_ERROR(req, err);
+    return req->result;
+  }
+
+  POST;
+}
+
+
+int uv_fs_rmdir(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) {
+  int err;
+
+  INIT(UV_FS_RMDIR);
+  err = fs__capture_path(req, path, NULL, cb != NULL);
+  if (err) {
+    SET_REQ_WIN32_ERROR(req, err);
+    return req->result;
+  }
+
+  POST;
+}
+
+
+int uv_fs_scandir(uv_loop_t* loop, uv_fs_t* req, const char* path, int flags,
+    uv_fs_cb cb) {
+  int err;
+
+  INIT(UV_FS_SCANDIR);
+  err = fs__capture_path(req, path, NULL, cb != NULL);
+  if (err) {
+    SET_REQ_WIN32_ERROR(req, err);
+    return req->result;
+  }
+
+  req->fs.info.file_flags = flags;
+  POST;
+}
+
+int uv_fs_opendir(uv_loop_t* loop,
+                  uv_fs_t* req,
+                  const char* path,
+                  uv_fs_cb cb) {
+  int err;
+
+  INIT(UV_FS_OPENDIR);
+  err = fs__capture_path(req, path, NULL, cb != NULL);
+  if (err) {
+    SET_REQ_WIN32_ERROR(req, err);
+    return req->result;
+  }
+  POST;
+}
+
+int uv_fs_readdir(uv_loop_t* loop,
+                  uv_fs_t* req,
+                  uv_dir_t* dir,
+                  uv_fs_cb cb) {
+  INIT(UV_FS_READDIR);
+
+  if (dir == NULL ||
+      dir->dirents == NULL ||
+      dir->dir_handle == INVALID_HANDLE_VALUE) {
+    SET_REQ_UV_ERROR(req, UV_EINVAL, ERROR_INVALID_PARAMETER);
+    return UV_EINVAL;
+  }
+
+  req->ptr = dir;
+  POST;
+}
+
+int uv_fs_closedir(uv_loop_t* loop,
+                   uv_fs_t* req,
+                   uv_dir_t* dir,
+                   uv_fs_cb cb) {
+  INIT(UV_FS_CLOSEDIR);
+  if (dir == NULL) {
+    SET_REQ_UV_ERROR(req, UV_EINVAL, ERROR_INVALID_PARAMETER);
+    return UV_EINVAL;
+  }
+  req->ptr = dir;
+  POST;
+}
+
+int uv_fs_link(uv_loop_t* loop, uv_fs_t* req, const char* path,
+    const char* new_path, uv_fs_cb cb) {
+  int err;
+
+  INIT(UV_FS_LINK);
+  err = fs__capture_path(req, path, new_path, cb != NULL);
+  if (err) {
+    SET_REQ_WIN32_ERROR(req, err);
+    return req->result;
+  }
+
+  POST;
+}
+
+
+int uv_fs_symlink(uv_loop_t* loop, uv_fs_t* req, const char* path,
+    const char* new_path, int flags, uv_fs_cb cb) {
+  int err;
+
+  INIT(UV_FS_SYMLINK);
+  err = fs__capture_path(req, path, new_path, cb != NULL);
+  if (err) {
+    SET_REQ_WIN32_ERROR(req, err);
+    return req->result;
+  }
+
+  req->fs.info.file_flags = flags;
+  POST;
+}
+
+
+int uv_fs_readlink(uv_loop_t* loop, uv_fs_t* req, const char* path,
+    uv_fs_cb cb) {
+  int err;
+
+  INIT(UV_FS_READLINK);
+  err = fs__capture_path(req, path, NULL, cb != NULL);
+  if (err) {
+    SET_REQ_WIN32_ERROR(req, err);
+    return req->result;
+  }
+
+  POST;
+}
+
+
+int uv_fs_realpath(uv_loop_t* loop, uv_fs_t* req, const char* path,
+    uv_fs_cb cb) {
+  int err;
+
+  INIT(UV_FS_REALPATH);
+
+  if (!path) {
+    SET_REQ_UV_ERROR(req, UV_EINVAL, ERROR_INVALID_PARAMETER);
+    return UV_EINVAL;
+  }
+
+  err = fs__capture_path(req, path, NULL, cb != NULL);
+  if (err) {
+    SET_REQ_WIN32_ERROR(req, err);
+    return req->result;
+  }
+
+  POST;
+}
+
+
+int uv_fs_chown(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_uid_t uid,
+    uv_gid_t gid, uv_fs_cb cb) {
+  int err;
+
+  INIT(UV_FS_CHOWN);
+  err = fs__capture_path(req, path, NULL, cb != NULL);
+  if (err) {
+    SET_REQ_WIN32_ERROR(req, err);
+    return req->result;
+  }
+
+  POST;
+}
+
+
+int uv_fs_fchown(uv_loop_t* loop, uv_fs_t* req, uv_file fd, uv_uid_t uid,
+    uv_gid_t gid, uv_fs_cb cb) {
+  INIT(UV_FS_FCHOWN);
+  POST;
+}
+
+
+int uv_fs_lchown(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_uid_t uid,
+    uv_gid_t gid, uv_fs_cb cb) {
+  int err;
+
+  INIT(UV_FS_LCHOWN);
+  err = fs__capture_path(req, path, NULL, cb != NULL);
+  if (err) {
+    SET_REQ_WIN32_ERROR(req, err);
+    return req->result;
+  }
+
+  POST;
+}
+
+
+int uv_fs_stat(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) {
+  int err;
+
+  INIT(UV_FS_STAT);
+  err = fs__capture_path(req, path, NULL, cb != NULL);
+  if (err) {
+    SET_REQ_WIN32_ERROR(req, err);
+    return req->result;
+  }
+
+  POST;
+}
+
+
+int uv_fs_lstat(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) {
+  int err;
+
+  INIT(UV_FS_LSTAT);
+  err = fs__capture_path(req, path, NULL, cb != NULL);
+  if (err) {
+    SET_REQ_WIN32_ERROR(req, err);
+    return req->result;
+  }
+
+  POST;
+}
+
+
+int uv_fs_fstat(uv_loop_t* loop, uv_fs_t* req, uv_file fd, uv_fs_cb cb) {
+  INIT(UV_FS_FSTAT);
+  req->file.fd = fd;
+  POST;
+}
+
+
+int uv_fs_rename(uv_loop_t* loop, uv_fs_t* req, const char* path,
+    const char* new_path, uv_fs_cb cb) {
+  int err;
+
+  INIT(UV_FS_RENAME);
+  err = fs__capture_path(req, path, new_path, cb != NULL);
+  if (err) {
+    SET_REQ_WIN32_ERROR(req, err);
+    return req->result;
+  }
+
+  POST;
+}
+
+
+int uv_fs_fsync(uv_loop_t* loop, uv_fs_t* req, uv_file fd, uv_fs_cb cb) {
+  INIT(UV_FS_FSYNC);
+  req->file.fd = fd;
+  POST;
+}
+
+
+int uv_fs_fdatasync(uv_loop_t* loop, uv_fs_t* req, uv_file fd, uv_fs_cb cb) {
+  INIT(UV_FS_FDATASYNC);
+  req->file.fd = fd;
+  POST;
+}
+
+
+int uv_fs_ftruncate(uv_loop_t* loop, uv_fs_t* req, uv_file fd,
+    int64_t offset, uv_fs_cb cb) {
+  INIT(UV_FS_FTRUNCATE);
+  req->file.fd = fd;
+  req->fs.info.offset = offset;
+  POST;
+}
+
+
+int uv_fs_copyfile(uv_loop_t* loop,
+                   uv_fs_t* req,
+                   const char* path,
+                   const char* new_path,
+                   int flags,
+                   uv_fs_cb cb) {
+  int err;
+
+  INIT(UV_FS_COPYFILE);
+
+  if (flags & ~(UV_FS_COPYFILE_EXCL |
+                UV_FS_COPYFILE_FICLONE |
+                UV_FS_COPYFILE_FICLONE_FORCE)) {
+    SET_REQ_UV_ERROR(req, UV_EINVAL, ERROR_INVALID_PARAMETER);
+    return UV_EINVAL;
+  }
+
+  err = fs__capture_path(req, path, new_path, cb != NULL);
+  if (err) {
+    SET_REQ_WIN32_ERROR(req, err);
+    return req->result;
+  }
+
+  req->fs.info.file_flags = flags;
+  POST;
+}
+
+
+int uv_fs_sendfile(uv_loop_t* loop, uv_fs_t* req, uv_file fd_out,
+    uv_file fd_in, int64_t in_offset, size_t length, uv_fs_cb cb) {
+  INIT(UV_FS_SENDFILE);
+  req->file.fd = fd_in;
+  req->fs.info.fd_out = fd_out;
+  req->fs.info.offset = in_offset;
+  req->fs.info.bufsml[0].len = length;
+  POST;
+}
+
+
+int uv_fs_access(uv_loop_t* loop,
+                 uv_fs_t* req,
+                 const char* path,
+                 int flags,
+                 uv_fs_cb cb) {
+  int err;
+
+  INIT(UV_FS_ACCESS);
+  err = fs__capture_path(req, path, NULL, cb != NULL);
+  if (err) {
+    SET_REQ_WIN32_ERROR(req, err);
+    return req->result;
+  }
+
+  req->fs.info.mode = flags;
+  POST;
+}
+
+
+int uv_fs_chmod(uv_loop_t* loop, uv_fs_t* req, const char* path, int mode,
+    uv_fs_cb cb) {
+  int err;
+
+  INIT(UV_FS_CHMOD);
+  err = fs__capture_path(req, path, NULL, cb != NULL);
+  if (err) {
+    SET_REQ_WIN32_ERROR(req, err);
+    return req->result;
+  }
+
+  req->fs.info.mode = mode;
+  POST;
+}
+
+
+int uv_fs_fchmod(uv_loop_t* loop, uv_fs_t* req, uv_file fd, int mode,
+    uv_fs_cb cb) {
+  INIT(UV_FS_FCHMOD);
+  req->file.fd = fd;
+  req->fs.info.mode = mode;
+  POST;
+}
+
+
+int uv_fs_utime(uv_loop_t* loop, uv_fs_t* req, const char* path, double atime,
+    double mtime, uv_fs_cb cb) {
+  int err;
+
+  INIT(UV_FS_UTIME);
+  err = fs__capture_path(req, path, NULL, cb != NULL);
+  if (err) {
+    SET_REQ_WIN32_ERROR(req, err);
+    return req->result;
+  }
+
+  req->fs.time.atime = atime;
+  req->fs.time.mtime = mtime;
+  POST;
+}
+
+
+int uv_fs_futime(uv_loop_t* loop, uv_fs_t* req, uv_file fd, double atime,
+    double mtime, uv_fs_cb cb) {
+  INIT(UV_FS_FUTIME);
+  req->file.fd = fd;
+  req->fs.time.atime = atime;
+  req->fs.time.mtime = mtime;
+  POST;
+}
+
+int uv_fs_lutime(uv_loop_t* loop, uv_fs_t* req, const char* path, double atime,
+    double mtime, uv_fs_cb cb) {
+  int err;
+
+  INIT(UV_FS_LUTIME);
+  err = fs__capture_path(req, path, NULL, cb != NULL);
+  if (err) {
+    SET_REQ_WIN32_ERROR(req, err);
+    return req->result;
+  }
+
+  req->fs.time.atime = atime;
+  req->fs.time.mtime = mtime;
+  POST;
+}
+
+
+int uv_fs_statfs(uv_loop_t* loop,
+                 uv_fs_t* req,
+                 const char* path,
+                 uv_fs_cb cb) {
+  int err;
+
+  INIT(UV_FS_STATFS);
+  err = fs__capture_path(req, path, NULL, cb != NULL);
+  if (err) {
+    SET_REQ_WIN32_ERROR(req, err);
+    return req->result;
+  }
+
+  POST;
+}
+
+int uv_fs_get_system_error(const uv_fs_t* req) {
+  return req->sys_errno_;
+}
diff --git a/wpinet/src/main/native/thirdparty/libuv/src/win/getaddrinfo.cpp b/wpinet/src/main/native/thirdparty/libuv/src/win/getaddrinfo.cpp
new file mode 100644
index 0000000..dfab860
--- /dev/null
+++ b/wpinet/src/main/native/thirdparty/libuv/src/win/getaddrinfo.cpp
@@ -0,0 +1,463 @@
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include <assert.h>
+
+#include "uv.h"
+#include "internal.h"
+#include "req-inl.h"
+#include "idna.h"
+
+/* EAI_* constants. */
+#include <winsock2.h>
+
+/* Needed for ConvertInterfaceIndexToLuid and ConvertInterfaceLuidToNameA */
+#include <iphlpapi.h>
+
+int uv__getaddrinfo_translate_error(int sys_err) {
+  switch (sys_err) {
+    case 0:                       return 0;
+    case WSATRY_AGAIN:            return UV_EAI_AGAIN;
+    case WSAEINVAL:               return UV_EAI_BADFLAGS;
+    case WSANO_RECOVERY:          return UV_EAI_FAIL;
+    case WSAEAFNOSUPPORT:         return UV_EAI_FAMILY;
+    case WSA_NOT_ENOUGH_MEMORY:   return UV_EAI_MEMORY;
+    case WSAHOST_NOT_FOUND:       return UV_EAI_NONAME;
+    case WSATYPE_NOT_FOUND:       return UV_EAI_SERVICE;
+    case WSAESOCKTNOSUPPORT:      return UV_EAI_SOCKTYPE;
+    default:                      return uv_translate_sys_error(sys_err);
+  }
+}
+
+
+/*
+ * MinGW is missing this
+ */
+#if !defined(_MSC_VER) && !defined(__MINGW64_VERSION_MAJOR)
+  typedef struct addrinfoW {
+    int ai_flags;
+    int ai_family;
+    int ai_socktype;
+    int ai_protocol;
+    size_t ai_addrlen;
+    WCHAR* ai_canonname;
+    struct sockaddr* ai_addr;
+    struct addrinfoW* ai_next;
+  } ADDRINFOW, *PADDRINFOW;
+
+  DECLSPEC_IMPORT int WSAAPI GetAddrInfoW(const WCHAR* node,
+                                          const WCHAR* service,
+                                          const ADDRINFOW* hints,
+                                          PADDRINFOW* result);
+
+  DECLSPEC_IMPORT void WSAAPI FreeAddrInfoW(PADDRINFOW pAddrInfo);
+#endif
+
+
+/* Adjust size value to be multiple of 4. Use to keep pointer aligned.
+ * Do we need different versions of this for different architectures? */
+#define ALIGNED_SIZE(X)     ((((X) + 3) >> 2) << 2)
+
+#ifndef NDIS_IF_MAX_STRING_SIZE
+#define NDIS_IF_MAX_STRING_SIZE IF_MAX_STRING_SIZE
+#endif
+
+static void uv__getaddrinfo_work(struct uv__work* w) {
+  uv_getaddrinfo_t* req;
+  struct addrinfoW* hints;
+  int err;
+
+  req = container_of(w, uv_getaddrinfo_t, work_req);
+  hints = req->addrinfow;
+  req->addrinfow = NULL;
+  err = GetAddrInfoW(req->node, req->service, hints, &req->addrinfow);
+  req->retcode = uv__getaddrinfo_translate_error(err);
+}
+
+
+/*
+ * Called from uv_run when complete. Call user specified callback
+ * then free returned addrinfo
+ * Returned addrinfo strings are converted from UTF-16 to UTF-8.
+ *
+ * To minimize allocation we calculate total size required,
+ * and copy all structs and referenced strings into the one block.
+ * Each size calculation is adjusted to avoid unaligned pointers.
+ */
+static void uv__getaddrinfo_done(struct uv__work* w, int status) {
+  uv_getaddrinfo_t* req;
+  int addrinfo_len = 0;
+  int name_len = 0;
+  size_t addrinfo_struct_len = ALIGNED_SIZE(sizeof(struct addrinfo));
+  struct addrinfoW* addrinfow_ptr;
+  struct addrinfo* addrinfo_ptr;
+  char* alloc_ptr = NULL;
+  char* cur_ptr = NULL;
+
+  req = container_of(w, uv_getaddrinfo_t, work_req);
+
+  /* release input parameter memory */
+  uv__free(req->alloc);
+  req->alloc = NULL;
+
+  if (status == UV_ECANCELED) {
+    assert(req->retcode == 0);
+    req->retcode = UV_EAI_CANCELED;
+    goto complete;
+  }
+
+  if (req->retcode == 0) {
+    /* Convert addrinfoW to addrinfo. First calculate required length. */
+    addrinfow_ptr = req->addrinfow;
+    while (addrinfow_ptr != NULL) {
+      addrinfo_len += addrinfo_struct_len +
+          ALIGNED_SIZE(addrinfow_ptr->ai_addrlen);
+      if (addrinfow_ptr->ai_canonname != NULL) {
+        name_len = WideCharToMultiByte(CP_UTF8,
+                                       0,
+                                       addrinfow_ptr->ai_canonname,
+                                       -1,
+                                       NULL,
+                                       0,
+                                       NULL,
+                                       NULL);
+        if (name_len == 0) {
+          req->retcode = uv_translate_sys_error(GetLastError());
+          goto complete;
+        }
+        addrinfo_len += ALIGNED_SIZE(name_len);
+      }
+      addrinfow_ptr = addrinfow_ptr->ai_next;
+    }
+
+    /* allocate memory for addrinfo results */
+    alloc_ptr = (char*)uv__malloc(addrinfo_len);
+
+    /* do conversions */
+    if (alloc_ptr != NULL) {
+      cur_ptr = alloc_ptr;
+      addrinfow_ptr = req->addrinfow;
+
+      while (addrinfow_ptr != NULL) {
+        /* copy addrinfo struct data */
+        assert(cur_ptr + addrinfo_struct_len <= alloc_ptr + addrinfo_len);
+        addrinfo_ptr = (struct addrinfo*)cur_ptr;
+        addrinfo_ptr->ai_family = addrinfow_ptr->ai_family;
+        addrinfo_ptr->ai_socktype = addrinfow_ptr->ai_socktype;
+        addrinfo_ptr->ai_protocol = addrinfow_ptr->ai_protocol;
+        addrinfo_ptr->ai_flags = addrinfow_ptr->ai_flags;
+        addrinfo_ptr->ai_addrlen = addrinfow_ptr->ai_addrlen;
+        addrinfo_ptr->ai_canonname = NULL;
+        addrinfo_ptr->ai_addr = NULL;
+        addrinfo_ptr->ai_next = NULL;
+
+        cur_ptr += addrinfo_struct_len;
+
+        /* copy sockaddr */
+        if (addrinfo_ptr->ai_addrlen > 0) {
+          assert(cur_ptr + addrinfo_ptr->ai_addrlen <=
+                 alloc_ptr + addrinfo_len);
+          memcpy(cur_ptr, addrinfow_ptr->ai_addr, addrinfo_ptr->ai_addrlen);
+          addrinfo_ptr->ai_addr = (struct sockaddr*)cur_ptr;
+          cur_ptr += ALIGNED_SIZE(addrinfo_ptr->ai_addrlen);
+        }
+
+        /* convert canonical name to UTF-8 */
+        if (addrinfow_ptr->ai_canonname != NULL) {
+          name_len = WideCharToMultiByte(CP_UTF8,
+                                         0,
+                                         addrinfow_ptr->ai_canonname,
+                                         -1,
+                                         NULL,
+                                         0,
+                                         NULL,
+                                         NULL);
+          assert(name_len > 0);
+          assert(cur_ptr + name_len <= alloc_ptr + addrinfo_len);
+          name_len = WideCharToMultiByte(CP_UTF8,
+                                         0,
+                                         addrinfow_ptr->ai_canonname,
+                                         -1,
+                                         cur_ptr,
+                                         name_len,
+                                         NULL,
+                                         NULL);
+          assert(name_len > 0);
+          addrinfo_ptr->ai_canonname = cur_ptr;
+          cur_ptr += ALIGNED_SIZE(name_len);
+        }
+        assert(cur_ptr <= alloc_ptr + addrinfo_len);
+
+        /* set next ptr */
+        addrinfow_ptr = addrinfow_ptr->ai_next;
+        if (addrinfow_ptr != NULL) {
+          addrinfo_ptr->ai_next = (struct addrinfo*)cur_ptr;
+        }
+      }
+      req->addrinfo = (struct addrinfo*)alloc_ptr;
+    } else {
+      req->retcode = UV_EAI_MEMORY;
+    }
+  }
+
+  /* return memory to system */
+  if (req->addrinfow != NULL) {
+    FreeAddrInfoW(req->addrinfow);
+    req->addrinfow = NULL;
+  }
+
+complete:
+  uv__req_unregister(req->loop, req);
+
+  /* finally do callback with converted result */
+  if (req->getaddrinfo_cb)
+    req->getaddrinfo_cb(req, req->retcode, req->addrinfo);
+}
+
+
+void uv_freeaddrinfo(struct addrinfo* ai) {
+  char* alloc_ptr = (char*)ai;
+
+  /* release copied result memory */
+  uv__free(alloc_ptr);
+}
+
+
+/*
+ * Entry point for getaddrinfo
+ * we convert the UTF-8 strings to UNICODE
+ * and save the UNICODE string pointers in the req
+ * We also copy hints so that caller does not need to keep memory until the
+ * callback.
+ * return 0 if a callback will be made
+ * return error code if validation fails
+ *
+ * To minimize allocation we calculate total size required,
+ * and copy all structs and referenced strings into the one block.
+ * Each size calculation is adjusted to avoid unaligned pointers.
+ */
+int uv_getaddrinfo(uv_loop_t* loop,
+                   uv_getaddrinfo_t* req,
+                   uv_getaddrinfo_cb getaddrinfo_cb,
+                   const char* node,
+                   const char* service,
+                   const struct addrinfo* hints) {
+  char hostname_ascii[256];
+  int nodesize = 0;
+  int servicesize = 0;
+  int hintssize = 0;
+  char* alloc_ptr = NULL;
+  int err;
+  long rc;
+
+  if (req == NULL || (node == NULL && service == NULL)) {
+    return UV_EINVAL;
+  }
+
+  UV_REQ_INIT(req, UV_GETADDRINFO);
+  req->getaddrinfo_cb = getaddrinfo_cb;
+  req->addrinfo = NULL;
+  req->loop = loop;
+  req->retcode = 0;
+
+  /* calculate required memory size for all input values */
+  if (node != NULL) {
+    rc = uv__idna_toascii(node,
+                          node + strlen(node),
+                          hostname_ascii,
+                          hostname_ascii + sizeof(hostname_ascii));
+    if (rc < 0)
+      return rc;
+    nodesize = ALIGNED_SIZE(MultiByteToWideChar(CP_UTF8, 0, hostname_ascii,
+                                                -1, NULL, 0) * sizeof(WCHAR));
+    if (nodesize == 0) {
+      err = GetLastError();
+      goto error;
+    }
+    node = hostname_ascii;
+  }
+
+  if (service != NULL) {
+    servicesize = ALIGNED_SIZE(MultiByteToWideChar(CP_UTF8,
+                                                   0,
+                                                   service,
+                                                   -1,
+                                                   NULL,
+                                                   0) *
+                               sizeof(WCHAR));
+    if (servicesize == 0) {
+      err = GetLastError();
+      goto error;
+    }
+  }
+  if (hints != NULL) {
+    hintssize = ALIGNED_SIZE(sizeof(struct addrinfoW));
+  }
+
+  /* allocate memory for inputs, and partition it as needed */
+  alloc_ptr = (char*)uv__malloc(nodesize + servicesize + hintssize);
+  if (!alloc_ptr) {
+    err = WSAENOBUFS;
+    goto error;
+  }
+
+  /* save alloc_ptr now so we can free if error */
+  req->alloc = (void*)alloc_ptr;
+
+  /* Convert node string to UTF16 into allocated memory and save pointer in the
+   * request. */
+  if (node != NULL) {
+    req->node = (WCHAR*)alloc_ptr;
+    if (MultiByteToWideChar(CP_UTF8,
+                            0,
+                            node,
+                            -1,
+                            (WCHAR*) alloc_ptr,
+                            nodesize / sizeof(WCHAR)) == 0) {
+      err = GetLastError();
+      goto error;
+    }
+    alloc_ptr += nodesize;
+  } else {
+    req->node = NULL;
+  }
+
+  /* Convert service string to UTF16 into allocated memory and save pointer in
+   * the req. */
+  if (service != NULL) {
+    req->service = (WCHAR*)alloc_ptr;
+    if (MultiByteToWideChar(CP_UTF8,
+                            0,
+                            service,
+                            -1,
+                            (WCHAR*) alloc_ptr,
+                            servicesize / sizeof(WCHAR)) == 0) {
+      err = GetLastError();
+      goto error;
+    }
+    alloc_ptr += servicesize;
+  } else {
+    req->service = NULL;
+  }
+
+  /* copy hints to allocated memory and save pointer in req */
+  if (hints != NULL) {
+    req->addrinfow = (struct addrinfoW*)alloc_ptr;
+    req->addrinfow->ai_family = hints->ai_family;
+    req->addrinfow->ai_socktype = hints->ai_socktype;
+    req->addrinfow->ai_protocol = hints->ai_protocol;
+    req->addrinfow->ai_flags = hints->ai_flags;
+    req->addrinfow->ai_addrlen = 0;
+    req->addrinfow->ai_canonname = NULL;
+    req->addrinfow->ai_addr = NULL;
+    req->addrinfow->ai_next = NULL;
+  } else {
+    req->addrinfow = NULL;
+  }
+
+  uv__req_register(loop, req);
+
+  if (getaddrinfo_cb) {
+    uv__work_submit(loop,
+                    &req->work_req,
+                    UV__WORK_SLOW_IO,
+                    uv__getaddrinfo_work,
+                    uv__getaddrinfo_done);
+    return 0;
+  } else {
+    uv__getaddrinfo_work(&req->work_req);
+    uv__getaddrinfo_done(&req->work_req, 0);
+    return req->retcode;
+  }
+
+error:
+  if (req != NULL) {
+    uv__free(req->alloc);
+    req->alloc = NULL;
+  }
+  return uv_translate_sys_error(err);
+}
+
+int uv_if_indextoname(unsigned int ifindex, char* buffer, size_t* size) {
+  NET_LUID luid;
+  wchar_t wname[NDIS_IF_MAX_STRING_SIZE + 1]; /* Add one for the NUL. */
+  DWORD bufsize;
+  int r;
+
+  if (buffer == NULL || size == NULL || *size == 0)
+    return UV_EINVAL;
+
+  r = ConvertInterfaceIndexToLuid(ifindex, &luid);
+
+  if (r != 0)
+    return uv_translate_sys_error(r);
+
+  r = ConvertInterfaceLuidToNameW(&luid, wname, ARRAY_SIZE(wname));
+
+  if (r != 0)
+    return uv_translate_sys_error(r);
+
+  /* Check how much space we need */
+  bufsize = WideCharToMultiByte(CP_UTF8, 0, wname, -1, NULL, 0, NULL, NULL);
+
+  if (bufsize == 0) {
+    return uv_translate_sys_error(GetLastError());
+  } else if (bufsize > *size) {
+    *size = bufsize;
+    return UV_ENOBUFS;
+  }
+
+  /* Convert to UTF-8 */
+  bufsize = WideCharToMultiByte(CP_UTF8,
+                                0,
+                                wname,
+                                -1,
+                                buffer,
+                                *size,
+                                NULL,
+                                NULL);
+
+  if (bufsize == 0)
+    return uv_translate_sys_error(GetLastError());
+
+  *size = bufsize - 1;
+  return 0;
+}
+
+int uv_if_indextoiid(unsigned int ifindex, char* buffer, size_t* size) {
+  int r;
+
+  if (buffer == NULL || size == NULL || *size == 0)
+    return UV_EINVAL;
+
+  r = snprintf(buffer, *size, "%d", ifindex);
+
+  if (r < 0)
+    return uv_translate_sys_error(r);
+
+  if (r >= (int) *size) {
+    *size = r + 1;
+    return UV_ENOBUFS;
+  }
+
+  *size = r;
+  return 0;
+}
diff --git a/wpinet/src/main/native/thirdparty/libuv/src/win/getnameinfo.cpp b/wpinet/src/main/native/thirdparty/libuv/src/win/getnameinfo.cpp
new file mode 100644
index 0000000..b377338
--- /dev/null
+++ b/wpinet/src/main/native/thirdparty/libuv/src/win/getnameinfo.cpp
@@ -0,0 +1,157 @@
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+*
+* Permission is hereby granted, free of charge, to any person obtaining a copy
+* of this software and associated documentation files (the "Software"), to
+* deal in the Software without restriction, including without limitation the
+* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+* sell copies of the Software, and to permit persons to whom the Software is
+* furnished to do so, subject to the following conditions:
+*
+* The above copyright notice and this permission notice shall be included in
+* all copies or substantial portions of the Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+* IN THE SOFTWARE.
+*/
+
+#include <assert.h>
+#include <stdio.h>
+
+#include "uv.h"
+#include "internal.h"
+#include "req-inl.h"
+
+#ifndef GetNameInfo
+int WSAAPI GetNameInfoW(
+  const SOCKADDR *pSockaddr,
+  socklen_t SockaddrLength,
+  PWCHAR pNodeBuffer,
+  DWORD NodeBufferSize,
+  PWCHAR pServiceBuffer,
+  DWORD ServiceBufferSize,
+  INT Flags
+);
+#endif
+
+static void uv__getnameinfo_work(struct uv__work* w) {
+  uv_getnameinfo_t* req;
+  WCHAR host[NI_MAXHOST];
+  WCHAR service[NI_MAXSERV];
+  int ret;
+
+  req = container_of(w, uv_getnameinfo_t, work_req);
+  if (GetNameInfoW((struct sockaddr*)&req->storage,
+                   sizeof(req->storage),
+                   host,
+                   ARRAY_SIZE(host),
+                   service,
+                   ARRAY_SIZE(service),
+                   req->flags)) {
+    ret = WSAGetLastError();
+    req->retcode = uv__getaddrinfo_translate_error(ret);
+    return;
+  }
+
+  ret = WideCharToMultiByte(CP_UTF8,
+                            0,
+                            host,
+                            -1,
+                            req->host,
+                            sizeof(req->host),
+                            NULL,
+                            NULL);
+  if (ret == 0) {
+    req->retcode = uv_translate_sys_error(GetLastError());
+    return;
+  }
+
+  ret = WideCharToMultiByte(CP_UTF8,
+                            0,
+                            service,
+                            -1,
+                            req->service,
+                            sizeof(req->service),
+                            NULL,
+                            NULL);
+  if (ret == 0) {
+    req->retcode = uv_translate_sys_error(GetLastError());
+  }
+}
+
+
+/*
+* Called from uv_run when complete.
+*/
+static void uv__getnameinfo_done(struct uv__work* w, int status) {
+  uv_getnameinfo_t* req;
+  char* host;
+  char* service;
+
+  req = container_of(w, uv_getnameinfo_t, work_req);
+  uv__req_unregister(req->loop, req);
+  host = service = NULL;
+
+  if (status == UV_ECANCELED) {
+    assert(req->retcode == 0);
+    req->retcode = UV_EAI_CANCELED;
+  } else if (req->retcode == 0) {
+    host = req->host;
+    service = req->service;
+  }
+
+  if (req->getnameinfo_cb)
+    req->getnameinfo_cb(req, req->retcode, host, service);
+}
+
+
+/*
+* Entry point for getnameinfo
+* return 0 if a callback will be made
+* return error code if validation fails
+*/
+int uv_getnameinfo(uv_loop_t* loop,
+                   uv_getnameinfo_t* req,
+                   uv_getnameinfo_cb getnameinfo_cb,
+                   const struct sockaddr* addr,
+                   int flags) {
+  if (req == NULL || addr == NULL)
+    return UV_EINVAL;
+
+  if (addr->sa_family == AF_INET) {
+    memcpy(&req->storage,
+           addr,
+           sizeof(struct sockaddr_in));
+  } else if (addr->sa_family == AF_INET6) {
+    memcpy(&req->storage,
+           addr,
+           sizeof(struct sockaddr_in6));
+  } else {
+    return UV_EINVAL;
+  }
+
+  UV_REQ_INIT(req, UV_GETNAMEINFO);
+  uv__req_register(loop, req);
+
+  req->getnameinfo_cb = getnameinfo_cb;
+  req->flags = flags;
+  req->loop = loop;
+  req->retcode = 0;
+
+  if (getnameinfo_cb) {
+    uv__work_submit(loop,
+                    &req->work_req,
+                    UV__WORK_SLOW_IO,
+                    uv__getnameinfo_work,
+                    uv__getnameinfo_done);
+    return 0;
+  } else {
+    uv__getnameinfo_work(&req->work_req);
+    uv__getnameinfo_done(&req->work_req, 0);
+    return req->retcode;
+  }
+}
diff --git a/wpinet/src/main/native/thirdparty/libuv/src/win/handle-inl.h b/wpinet/src/main/native/thirdparty/libuv/src/win/handle-inl.h
new file mode 100644
index 0000000..5c843c2
--- /dev/null
+++ b/wpinet/src/main/native/thirdparty/libuv/src/win/handle-inl.h
@@ -0,0 +1,180 @@
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#ifndef UV_WIN_HANDLE_INL_H_
+#define UV_WIN_HANDLE_INL_H_
+
+#include <assert.h>
+#include <io.h>
+
+#include "uv.h"
+#include "internal.h"
+
+
+#define DECREASE_ACTIVE_COUNT(loop, handle)                             \
+  do {                                                                  \
+    if (--(handle)->activecnt == 0 &&                                   \
+        !((handle)->flags & UV_HANDLE_CLOSING)) {                       \
+      uv__handle_stop((handle));                                        \
+    }                                                                   \
+    assert((handle)->activecnt >= 0);                                   \
+  } while (0)
+
+
+#define INCREASE_ACTIVE_COUNT(loop, handle)                             \
+  do {                                                                  \
+    if ((handle)->activecnt++ == 0) {                                   \
+      uv__handle_start((handle));                                       \
+    }                                                                   \
+    assert((handle)->activecnt > 0);                                    \
+  } while (0)
+
+
+#define DECREASE_PENDING_REQ_COUNT(handle)                              \
+  do {                                                                  \
+    assert(handle->reqs_pending > 0);                                   \
+    handle->reqs_pending--;                                             \
+                                                                        \
+    if (handle->flags & UV_HANDLE_CLOSING &&                            \
+        handle->reqs_pending == 0) {                                    \
+      uv__want_endgame(loop, (uv_handle_t*)handle);                     \
+    }                                                                   \
+  } while (0)
+
+
+#define uv__handle_closing(handle)                                      \
+  do {                                                                  \
+    assert(!((handle)->flags & UV_HANDLE_CLOSING));                     \
+                                                                        \
+    if (!(((handle)->flags & UV_HANDLE_ACTIVE) &&                       \
+          ((handle)->flags & UV_HANDLE_REF)))                           \
+      uv__active_handle_add((uv_handle_t*) (handle));                   \
+                                                                        \
+    (handle)->flags |= UV_HANDLE_CLOSING;                               \
+    (handle)->flags &= ~UV_HANDLE_ACTIVE;                               \
+  } while (0)
+
+
+#define uv__handle_close(handle)                                        \
+  do {                                                                  \
+    QUEUE_REMOVE(&(handle)->handle_queue);                              \
+    uv__active_handle_rm((uv_handle_t*) (handle));                      \
+                                                                        \
+    (handle)->flags |= UV_HANDLE_CLOSED;                                \
+                                                                        \
+    if ((handle)->close_cb)                                             \
+      (handle)->close_cb((uv_handle_t*) (handle));                      \
+  } while (0)
+
+
+INLINE static void uv__want_endgame(uv_loop_t* loop, uv_handle_t* handle) {
+  if (!(handle->flags & UV_HANDLE_ENDGAME_QUEUED)) {
+    handle->flags |= UV_HANDLE_ENDGAME_QUEUED;
+
+    handle->endgame_next = loop->endgame_handles;
+    loop->endgame_handles = handle;
+  }
+}
+
+
+INLINE static void uv__process_endgames(uv_loop_t* loop) {
+  uv_handle_t* handle;
+
+  while (loop->endgame_handles) {
+    handle = loop->endgame_handles;
+    loop->endgame_handles = handle->endgame_next;
+
+    handle->flags &= ~UV_HANDLE_ENDGAME_QUEUED;
+
+    switch (handle->type) {
+      case UV_TCP:
+        uv__tcp_endgame(loop, (uv_tcp_t*) handle);
+        break;
+
+      case UV_NAMED_PIPE:
+        uv__pipe_endgame(loop, (uv_pipe_t*) handle);
+        break;
+
+      case UV_TTY:
+        uv__tty_endgame(loop, (uv_tty_t*) handle);
+        break;
+
+      case UV_UDP:
+        uv__udp_endgame(loop, (uv_udp_t*) handle);
+        break;
+
+      case UV_POLL:
+        uv__poll_endgame(loop, (uv_poll_t*) handle);
+        break;
+
+      case UV_TIMER:
+        uv__timer_close((uv_timer_t*) handle);
+        uv__handle_close(handle);
+        break;
+
+      case UV_PREPARE:
+      case UV_CHECK:
+      case UV_IDLE:
+        uv__loop_watcher_endgame(loop, handle);
+        break;
+
+      case UV_ASYNC:
+        uv__async_endgame(loop, (uv_async_t*) handle);
+        break;
+
+      case UV_SIGNAL:
+        uv__signal_endgame(loop, (uv_signal_t*) handle);
+        break;
+
+      case UV_PROCESS:
+        uv__process_endgame(loop, (uv_process_t*) handle);
+        break;
+
+      case UV_FS_EVENT:
+        uv__fs_event_endgame(loop, (uv_fs_event_t*) handle);
+        break;
+
+      case UV_FS_POLL:
+        uv__fs_poll_endgame(loop, (uv_fs_poll_t*) handle);
+        break;
+
+      default:
+        assert(0);
+        break;
+    }
+  }
+}
+
+INLINE static HANDLE uv__get_osfhandle(int fd)
+{
+  /* _get_osfhandle() raises an assert in debug builds if the FD is invalid.
+   * But it also correctly checks the FD and returns INVALID_HANDLE_VALUE for
+   * invalid FDs in release builds (or if you let the assert continue). So this
+   * wrapper function disables asserts when calling _get_osfhandle. */
+
+  HANDLE handle;
+  UV_BEGIN_DISABLE_CRT_ASSERT();
+  handle = (HANDLE) _get_osfhandle(fd);
+  UV_END_DISABLE_CRT_ASSERT();
+  return handle;
+}
+
+#endif /* UV_WIN_HANDLE_INL_H_ */
diff --git a/wpinet/src/main/native/thirdparty/libuv/src/win/handle.cpp b/wpinet/src/main/native/thirdparty/libuv/src/win/handle.cpp
new file mode 100644
index 0000000..53a81fd
--- /dev/null
+++ b/wpinet/src/main/native/thirdparty/libuv/src/win/handle.cpp
@@ -0,0 +1,162 @@
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include <assert.h>
+#include <io.h>
+#include <stdlib.h>
+
+#include "uv.h"
+#include "internal.h"
+#include "handle-inl.h"
+
+
+uv_handle_type uv_guess_handle(uv_file file) {
+  HANDLE handle;
+  DWORD mode;
+
+  if (file < 0) {
+    return UV_UNKNOWN_HANDLE;
+  }
+
+  handle = uv__get_osfhandle(file);
+
+  switch (GetFileType(handle)) {
+    case FILE_TYPE_CHAR:
+      if (GetConsoleMode(handle, &mode)) {
+        return UV_TTY;
+      } else {
+        return UV_FILE;
+      }
+
+    case FILE_TYPE_PIPE:
+      return UV_NAMED_PIPE;
+
+    case FILE_TYPE_DISK:
+      return UV_FILE;
+
+    default:
+      return UV_UNKNOWN_HANDLE;
+  }
+}
+
+
+int uv_is_active(const uv_handle_t* handle) {
+  return (handle->flags & UV_HANDLE_ACTIVE) &&
+        !(handle->flags & UV_HANDLE_CLOSING);
+}
+
+
+void uv_close(uv_handle_t* handle, uv_close_cb cb) {
+  uv_loop_t* loop = handle->loop;
+
+  if (handle->flags & UV_HANDLE_CLOSING) {
+    assert(0);
+    return;
+  }
+
+  handle->close_cb = cb;
+
+  /* Handle-specific close actions */
+  switch (handle->type) {
+    case UV_TCP:
+      uv__tcp_close(loop, (uv_tcp_t*)handle);
+      return;
+
+    case UV_NAMED_PIPE:
+      uv__pipe_close(loop, (uv_pipe_t*) handle);
+      return;
+
+    case UV_TTY:
+      uv__tty_close((uv_tty_t*) handle);
+      return;
+
+    case UV_UDP:
+      uv__udp_close(loop, (uv_udp_t*) handle);
+      return;
+
+    case UV_POLL:
+      uv__poll_close(loop, (uv_poll_t*) handle);
+      return;
+
+    case UV_TIMER:
+      uv_timer_stop((uv_timer_t*)handle);
+      uv__handle_closing(handle);
+      uv__want_endgame(loop, handle);
+      return;
+
+    case UV_PREPARE:
+      uv_prepare_stop((uv_prepare_t*)handle);
+      uv__handle_closing(handle);
+      uv__want_endgame(loop, handle);
+      return;
+
+    case UV_CHECK:
+      uv_check_stop((uv_check_t*)handle);
+      uv__handle_closing(handle);
+      uv__want_endgame(loop, handle);
+      return;
+
+    case UV_IDLE:
+      uv_idle_stop((uv_idle_t*)handle);
+      uv__handle_closing(handle);
+      uv__want_endgame(loop, handle);
+      return;
+
+    case UV_ASYNC:
+      uv__async_close(loop, (uv_async_t*) handle);
+      return;
+
+    case UV_SIGNAL:
+      uv__signal_close(loop, (uv_signal_t*) handle);
+      return;
+
+    case UV_PROCESS:
+      uv__process_close(loop, (uv_process_t*) handle);
+      return;
+
+    case UV_FS_EVENT:
+      uv__fs_event_close(loop, (uv_fs_event_t*) handle);
+      return;
+
+    case UV_FS_POLL:
+      uv__fs_poll_close((uv_fs_poll_t*) handle);
+      uv__handle_closing(handle);
+      return;
+
+    default:
+      /* Not supported */
+      abort();
+  }
+}
+
+
+int uv_is_closing(const uv_handle_t* handle) {
+  return !!(handle->flags & (UV_HANDLE_CLOSING | UV_HANDLE_CLOSED));
+}
+
+
+uv_os_fd_t uv_get_osfhandle(int fd) {
+  return uv__get_osfhandle(fd);
+}
+
+int uv_open_osfhandle(uv_os_fd_t os_fd) {
+  return _open_osfhandle((intptr_t) os_fd, 0);
+}
diff --git a/wpinet/src/main/native/thirdparty/libuv/src/win/internal.h b/wpinet/src/main/native/thirdparty/libuv/src/win/internal.h
new file mode 100644
index 0000000..89c72b8
--- /dev/null
+++ b/wpinet/src/main/native/thirdparty/libuv/src/win/internal.h
@@ -0,0 +1,343 @@
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#ifndef UV_WIN_INTERNAL_H_
+#define UV_WIN_INTERNAL_H_
+
+#include "uv.h"
+#include "../uv-common.h"
+
+#include "uv/tree.h"
+#include "winapi.h"
+#include "winsock.h"
+
+#ifdef _MSC_VER
+# define INLINE __inline
+# define UV_THREAD_LOCAL __declspec( thread )
+#else
+# define INLINE inline
+# define UV_THREAD_LOCAL __thread
+#endif
+
+
+#ifdef _DEBUG
+
+extern UV_THREAD_LOCAL int uv__crt_assert_enabled;
+
+#define UV_BEGIN_DISABLE_CRT_ASSERT()                           \
+  {                                                             \
+    int uv__saved_crt_assert_enabled = uv__crt_assert_enabled;  \
+    uv__crt_assert_enabled = FALSE;
+
+
+#define UV_END_DISABLE_CRT_ASSERT()                             \
+    uv__crt_assert_enabled = uv__saved_crt_assert_enabled;      \
+  }
+
+#else
+#define UV_BEGIN_DISABLE_CRT_ASSERT()
+#define UV_END_DISABLE_CRT_ASSERT()
+#endif
+
+/*
+ * TCP
+ */
+
+typedef enum {
+  UV__IPC_SOCKET_XFER_NONE = 0,
+  UV__IPC_SOCKET_XFER_TCP_CONNECTION,
+  UV__IPC_SOCKET_XFER_TCP_SERVER
+} uv__ipc_socket_xfer_type_t;
+
+typedef struct {
+  WSAPROTOCOL_INFOW socket_info;
+  uint32_t delayed_error;
+} uv__ipc_socket_xfer_info_t;
+
+int uv__tcp_listen(uv_tcp_t* handle, int backlog, uv_connection_cb cb);
+int uv__tcp_accept(uv_tcp_t* server, uv_tcp_t* client);
+int uv__tcp_read_start(uv_tcp_t* handle, uv_alloc_cb alloc_cb,
+    uv_read_cb read_cb);
+int uv__tcp_write(uv_loop_t* loop, uv_write_t* req, uv_tcp_t* handle,
+    const uv_buf_t bufs[], unsigned int nbufs, uv_write_cb cb);
+int uv__tcp_try_write(uv_tcp_t* handle, const uv_buf_t bufs[],
+    unsigned int nbufs);
+
+void uv__process_tcp_read_req(uv_loop_t* loop, uv_tcp_t* handle, uv_req_t* req);
+void uv__process_tcp_write_req(uv_loop_t* loop, uv_tcp_t* handle,
+    uv_write_t* req);
+void uv__process_tcp_accept_req(uv_loop_t* loop, uv_tcp_t* handle,
+    uv_req_t* req);
+void uv__process_tcp_connect_req(uv_loop_t* loop, uv_tcp_t* handle,
+    uv_connect_t* req);
+void uv__process_tcp_shutdown_req(uv_loop_t* loop,
+                                  uv_tcp_t* stream,
+                                  uv_shutdown_t* req);
+
+void uv__tcp_close(uv_loop_t* loop, uv_tcp_t* tcp);
+void uv__tcp_endgame(uv_loop_t* loop, uv_tcp_t* handle);
+
+int uv__tcp_xfer_export(uv_tcp_t* handle,
+                        int pid,
+                        uv__ipc_socket_xfer_type_t* xfer_type,
+                        uv__ipc_socket_xfer_info_t* xfer_info);
+int uv__tcp_xfer_import(uv_tcp_t* tcp,
+                        uv__ipc_socket_xfer_type_t xfer_type,
+                        uv__ipc_socket_xfer_info_t* xfer_info);
+
+
+/*
+ * UDP
+ */
+void uv__process_udp_recv_req(uv_loop_t* loop, uv_udp_t* handle, uv_req_t* req);
+void uv__process_udp_send_req(uv_loop_t* loop, uv_udp_t* handle,
+    uv_udp_send_t* req);
+
+void uv__udp_close(uv_loop_t* loop, uv_udp_t* handle);
+void uv__udp_endgame(uv_loop_t* loop, uv_udp_t* handle);
+
+
+/*
+ * Pipes
+ */
+int uv__create_stdio_pipe_pair(uv_loop_t* loop,
+    uv_pipe_t* parent_pipe, HANDLE* child_pipe_ptr, unsigned int flags);
+
+int uv__pipe_listen(uv_pipe_t* handle, int backlog, uv_connection_cb cb);
+int uv__pipe_accept(uv_pipe_t* server, uv_stream_t* client);
+int uv__pipe_read_start(uv_pipe_t* handle, uv_alloc_cb alloc_cb,
+    uv_read_cb read_cb);
+void uv__pipe_read_stop(uv_pipe_t* handle);
+int uv__pipe_write(uv_loop_t* loop,
+                   uv_write_t* req,
+                   uv_pipe_t* handle,
+                   const uv_buf_t bufs[],
+                   size_t nbufs,
+                   uv_stream_t* send_handle,
+                   uv_write_cb cb);
+void uv__pipe_shutdown(uv_loop_t* loop, uv_pipe_t* handle, uv_shutdown_t* req);
+
+void uv__process_pipe_read_req(uv_loop_t* loop, uv_pipe_t* handle,
+    uv_req_t* req);
+void uv__process_pipe_write_req(uv_loop_t* loop, uv_pipe_t* handle,
+    uv_write_t* req);
+void uv__process_pipe_accept_req(uv_loop_t* loop, uv_pipe_t* handle,
+    uv_req_t* raw_req);
+void uv__process_pipe_connect_req(uv_loop_t* loop, uv_pipe_t* handle,
+    uv_connect_t* req);
+void uv__process_pipe_shutdown_req(uv_loop_t* loop, uv_pipe_t* handle,
+    uv_shutdown_t* req);
+
+void uv__pipe_close(uv_loop_t* loop, uv_pipe_t* handle);
+void uv__pipe_endgame(uv_loop_t* loop, uv_pipe_t* handle);
+
+
+/*
+ * TTY
+ */
+void uv__console_init(void);
+
+int uv__tty_read_start(uv_tty_t* handle, uv_alloc_cb alloc_cb,
+    uv_read_cb read_cb);
+int uv__tty_read_stop(uv_tty_t* handle);
+int uv__tty_write(uv_loop_t* loop, uv_write_t* req, uv_tty_t* handle,
+    const uv_buf_t bufs[], unsigned int nbufs, uv_write_cb cb);
+int uv__tty_try_write(uv_tty_t* handle, const uv_buf_t bufs[],
+    unsigned int nbufs);
+void uv__tty_close(uv_tty_t* handle);
+
+void uv__process_tty_read_req(uv_loop_t* loop, uv_tty_t* handle,
+    uv_req_t* req);
+void uv__process_tty_write_req(uv_loop_t* loop, uv_tty_t* handle,
+    uv_write_t* req);
+/*
+ * uv__process_tty_accept_req() is a stub to keep DELEGATE_STREAM_REQ working
+ * TODO: find a way to remove it
+ */
+void uv__process_tty_accept_req(uv_loop_t* loop, uv_tty_t* handle,
+    uv_req_t* raw_req);
+/*
+ * uv__process_tty_connect_req() is a stub to keep DELEGATE_STREAM_REQ working
+ * TODO: find a way to remove it
+ */
+void uv__process_tty_connect_req(uv_loop_t* loop, uv_tty_t* handle,
+    uv_connect_t* req);
+void uv__process_tty_shutdown_req(uv_loop_t* loop,
+                                  uv_tty_t* stream,
+                                  uv_shutdown_t* req);
+void uv__tty_endgame(uv_loop_t* loop, uv_tty_t* handle);
+
+
+/*
+ * Poll watchers
+ */
+void uv__process_poll_req(uv_loop_t* loop, uv_poll_t* handle,
+    uv_req_t* req);
+
+int uv__poll_close(uv_loop_t* loop, uv_poll_t* handle);
+void uv__poll_endgame(uv_loop_t* loop, uv_poll_t* handle);
+
+
+/*
+ * Loop watchers
+ */
+void uv__loop_watcher_endgame(uv_loop_t* loop, uv_handle_t* handle);
+
+void uv__prepare_invoke(uv_loop_t* loop);
+void uv__check_invoke(uv_loop_t* loop);
+void uv__idle_invoke(uv_loop_t* loop);
+
+void uv__once_init(void);
+
+
+/*
+ * Async watcher
+ */
+void uv__async_close(uv_loop_t* loop, uv_async_t* handle);
+void uv__async_endgame(uv_loop_t* loop, uv_async_t* handle);
+
+void uv__process_async_wakeup_req(uv_loop_t* loop, uv_async_t* handle,
+    uv_req_t* req);
+
+
+/*
+ * Signal watcher
+ */
+void uv__signals_init(void);
+int uv__signal_dispatch(int signum);
+
+void uv__signal_close(uv_loop_t* loop, uv_signal_t* handle);
+void uv__signal_endgame(uv_loop_t* loop, uv_signal_t* handle);
+
+void uv__process_signal_req(uv_loop_t* loop, uv_signal_t* handle,
+    uv_req_t* req);
+
+
+/*
+ * Spawn
+ */
+void uv__process_proc_exit(uv_loop_t* loop, uv_process_t* handle);
+void uv__process_close(uv_loop_t* loop, uv_process_t* handle);
+void uv__process_endgame(uv_loop_t* loop, uv_process_t* handle);
+
+
+/*
+ * FS
+ */
+void uv__fs_init(void);
+
+
+/*
+ * FS Event
+ */
+void uv__process_fs_event_req(uv_loop_t* loop, uv_req_t* req,
+    uv_fs_event_t* handle);
+void uv__fs_event_close(uv_loop_t* loop, uv_fs_event_t* handle);
+void uv__fs_event_endgame(uv_loop_t* loop, uv_fs_event_t* handle);
+
+
+/*
+ * Stat poller.
+ */
+void uv__fs_poll_endgame(uv_loop_t* loop, uv_fs_poll_t* handle);
+
+
+/*
+ * Utilities.
+ */
+void uv__util_init(void);
+
+uint64_t uv__hrtime(unsigned int scale);
+__declspec(noreturn) void uv_fatal_error(const int errorno, const char* syscall);
+int uv__getpwuid_r(uv_passwd_t* pwd);
+int uv__convert_utf16_to_utf8(const WCHAR* utf16, int utf16len, char** utf8);
+int uv__convert_utf8_to_utf16(const char* utf8, int utf8len, WCHAR** utf16);
+
+typedef int (WINAPI *uv__peersockfunc)(SOCKET, struct sockaddr*, int*);
+
+int uv__getsockpeername(const uv_handle_t* handle,
+                        uv__peersockfunc func,
+                        struct sockaddr* name,
+                        int* namelen,
+                        int delayed_error);
+
+int uv__random_rtlgenrandom(void* buf, size_t buflen);
+
+
+/*
+ * Process stdio handles.
+ */
+int uv__stdio_create(uv_loop_t* loop,
+                     const uv_process_options_t* options,
+                     BYTE** buffer_ptr);
+void uv__stdio_destroy(BYTE* buffer);
+void uv__stdio_noinherit(BYTE* buffer);
+int uv__stdio_verify(BYTE* buffer, WORD size);
+WORD uv__stdio_size(BYTE* buffer);
+HANDLE uv__stdio_handle(BYTE* buffer, int fd);
+
+
+/*
+ * Winapi and ntapi utility functions
+ */
+void uv__winapi_init(void);
+
+
+/*
+ * Winsock utility functions
+ */
+void uv__winsock_init(void);
+
+int uv__ntstatus_to_winsock_error(NTSTATUS status);
+
+BOOL uv__get_acceptex_function(SOCKET socket, LPFN_ACCEPTEX* target);
+BOOL uv__get_connectex_function(SOCKET socket, LPFN_CONNECTEX* target);
+
+int WSAAPI uv__wsarecv_workaround(SOCKET socket, WSABUF* buffers,
+    DWORD buffer_count, DWORD* bytes, DWORD* flags, WSAOVERLAPPED *overlapped,
+    LPWSAOVERLAPPED_COMPLETION_ROUTINE completion_routine);
+int WSAAPI uv__wsarecvfrom_workaround(SOCKET socket, WSABUF* buffers,
+    DWORD buffer_count, DWORD* bytes, DWORD* flags, struct sockaddr* addr,
+    int* addr_len, WSAOVERLAPPED *overlapped,
+    LPWSAOVERLAPPED_COMPLETION_ROUTINE completion_routine);
+
+int WSAAPI uv__msafd_poll(SOCKET socket, AFD_POLL_INFO* info_in,
+    AFD_POLL_INFO* info_out, OVERLAPPED* overlapped);
+
+/* Whether there are any non-IFS LSPs stacked on TCP */
+extern int uv_tcp_non_ifs_lsp_ipv4;
+extern int uv_tcp_non_ifs_lsp_ipv6;
+
+/* Ip address used to bind to any port at any interface */
+extern struct sockaddr_in uv_addr_ip4_any_;
+extern struct sockaddr_in6 uv_addr_ip6_any_;
+
+/*
+ * Wake all loops with fake message
+ */
+void uv__wake_all_loops(void);
+
+/*
+ * Init system wake-up detection
+ */
+void uv__init_detect_system_wakeup(void);
+
+#endif /* UV_WIN_INTERNAL_H_ */
diff --git a/wpinet/src/main/native/thirdparty/libuv/src/win/loop-watcher.cpp b/wpinet/src/main/native/thirdparty/libuv/src/win/loop-watcher.cpp
new file mode 100644
index 0000000..fad9e8a
--- /dev/null
+++ b/wpinet/src/main/native/thirdparty/libuv/src/win/loop-watcher.cpp
@@ -0,0 +1,122 @@
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include <assert.h>
+
+#include "uv.h"
+#include "internal.h"
+#include "handle-inl.h"
+
+
+void uv__loop_watcher_endgame(uv_loop_t* loop, uv_handle_t* handle) {
+  if (handle->flags & UV_HANDLE_CLOSING) {
+    assert(!(handle->flags & UV_HANDLE_CLOSED));
+    handle->flags |= UV_HANDLE_CLOSED;
+    uv__handle_close(handle);
+  }
+}
+
+
+#define UV_LOOP_WATCHER_DEFINE(name, NAME)                                    \
+  int uv_##name##_init(uv_loop_t* loop, uv_##name##_t* handle) {              \
+    uv__handle_init(loop, (uv_handle_t*) handle, UV_##NAME);                  \
+                                                                              \
+    return 0;                                                                 \
+  }                                                                           \
+                                                                              \
+                                                                              \
+  int uv_##name##_start(uv_##name##_t* handle, uv_##name##_cb cb) {           \
+    uv_loop_t* loop = handle->loop;                                           \
+    uv_##name##_t* old_head;                                                  \
+                                                                              \
+    assert(handle->type == UV_##NAME);                                        \
+                                                                              \
+    if (uv__is_active(handle))                                                \
+      return 0;                                                               \
+                                                                              \
+    if (cb == NULL)                                                           \
+      return UV_EINVAL;                                                       \
+                                                                              \
+    old_head = loop->name##_handles;                                          \
+                                                                              \
+    handle->name##_next = old_head;                                           \
+    handle->name##_prev = NULL;                                               \
+                                                                              \
+    if (old_head) {                                                           \
+      old_head->name##_prev = handle;                                         \
+    }                                                                         \
+                                                                              \
+    loop->name##_handles = handle;                                            \
+                                                                              \
+    handle->name##_cb = cb;                                                   \
+    uv__handle_start(handle);                                                 \
+                                                                              \
+    return 0;                                                                 \
+  }                                                                           \
+                                                                              \
+                                                                              \
+  int uv_##name##_stop(uv_##name##_t* handle) {                               \
+    uv_loop_t* loop = handle->loop;                                           \
+                                                                              \
+    assert(handle->type == UV_##NAME);                                        \
+                                                                              \
+    if (!uv__is_active(handle))                                               \
+      return 0;                                                               \
+                                                                              \
+    /* Update loop head if needed */                                          \
+    if (loop->name##_handles == handle) {                                     \
+      loop->name##_handles = handle->name##_next;                             \
+    }                                                                         \
+                                                                              \
+    /* Update the iterator-next pointer of needed */                          \
+    if (loop->next_##name##_handle == handle) {                               \
+      loop->next_##name##_handle = handle->name##_next;                       \
+    }                                                                         \
+                                                                              \
+    if (handle->name##_prev) {                                                \
+      handle->name##_prev->name##_next = handle->name##_next;                 \
+    }                                                                         \
+    if (handle->name##_next) {                                                \
+      handle->name##_next->name##_prev = handle->name##_prev;                 \
+    }                                                                         \
+                                                                              \
+    uv__handle_stop(handle);                                                  \
+                                                                              \
+    return 0;                                                                 \
+  }                                                                           \
+                                                                              \
+                                                                              \
+  void uv__##name##_invoke(uv_loop_t* loop) {                                 \
+    uv_##name##_t* handle;                                                    \
+                                                                              \
+    (loop)->next_##name##_handle = (loop)->name##_handles;                    \
+                                                                              \
+    while ((loop)->next_##name##_handle != NULL) {                            \
+      handle = (loop)->next_##name##_handle;                                  \
+      (loop)->next_##name##_handle = handle->name##_next;                     \
+                                                                              \
+      handle->name##_cb(handle);                                              \
+    }                                                                         \
+  }
+
+UV_LOOP_WATCHER_DEFINE(prepare, PREPARE)
+UV_LOOP_WATCHER_DEFINE(check, CHECK)
+UV_LOOP_WATCHER_DEFINE(idle, IDLE)
diff --git a/wpinet/src/main/native/thirdparty/libuv/src/win/pipe.cpp b/wpinet/src/main/native/thirdparty/libuv/src/win/pipe.cpp
new file mode 100644
index 0000000..f413a72
--- /dev/null
+++ b/wpinet/src/main/native/thirdparty/libuv/src/win/pipe.cpp
@@ -0,0 +1,2643 @@
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#define _CRT_NONSTDC_NO_WARNINGS
+
+#include <assert.h>
+#include <io.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "handle-inl.h"
+#include "internal.h"
+#include "req-inl.h"
+#include "stream-inl.h"
+#include "uv-common.h"
+#include "uv.h"
+
+#include <aclapi.h>
+#include <accctrl.h>
+
+/* A zero-size buffer for use by uv_pipe_read */
+static char uv_zero_[] = "";
+
+/* Null uv_buf_t */
+static const uv_buf_t uv_null_buf_ = { 0, NULL };
+
+/* The timeout that the pipe will wait for the remote end to write data when
+ * the local ends wants to shut it down. */
+static const int64_t eof_timeout = 50; /* ms */
+
+static const int default_pending_pipe_instances = 4;
+
+/* Pipe prefix */
+static char pipe_prefix[] = "\\\\?\\pipe";
+static const int pipe_prefix_len = sizeof(pipe_prefix) - 1;
+
+/* IPC incoming xfer queue item. */
+typedef struct {
+  uv__ipc_socket_xfer_type_t xfer_type;
+  uv__ipc_socket_xfer_info_t xfer_info;
+  QUEUE member;
+} uv__ipc_xfer_queue_item_t;
+
+/* IPC frame header flags. */
+/* clang-format off */
+enum {
+  UV__IPC_FRAME_HAS_DATA                = 0x01,
+  UV__IPC_FRAME_HAS_SOCKET_XFER         = 0x02,
+  UV__IPC_FRAME_XFER_IS_TCP_CONNECTION  = 0x04,
+  /* These are combinations of the flags above. */
+  UV__IPC_FRAME_XFER_FLAGS              = 0x06,
+  UV__IPC_FRAME_VALID_FLAGS             = 0x07
+};
+/* clang-format on */
+
+/* IPC frame header. */
+typedef struct {
+  uint32_t flags;
+  uint32_t reserved1;   /* Ignored. */
+  uint32_t data_length; /* Must be zero if there is no data. */
+  uint32_t reserved2;   /* Must be zero. */
+} uv__ipc_frame_header_t;
+
+/* To implement the IPC protocol correctly, these structures must have exactly
+ * the right size. */
+STATIC_ASSERT(sizeof(uv__ipc_frame_header_t) == 16);
+STATIC_ASSERT(sizeof(uv__ipc_socket_xfer_info_t) == 632);
+
+/* Coalesced write request. */
+typedef struct {
+  uv_write_t req;       /* Internal heap-allocated write request. */
+  uv_write_t* user_req; /* Pointer to user-specified uv_write_t. */
+} uv__coalesced_write_t;
+
+
+static void eof_timer_init(uv_pipe_t* pipe);
+static void eof_timer_start(uv_pipe_t* pipe);
+static void eof_timer_stop(uv_pipe_t* pipe);
+static void eof_timer_cb(uv_timer_t* timer);
+static void eof_timer_destroy(uv_pipe_t* pipe);
+static void eof_timer_close_cb(uv_handle_t* handle);
+
+
+static void uv__unique_pipe_name(char* ptr, char* name, size_t size) {
+  snprintf(name, size, "\\\\?\\pipe\\uv\\%p-%lu", ptr, GetCurrentProcessId());
+}
+
+
+int uv_pipe_init(uv_loop_t* loop, uv_pipe_t* handle, int ipc) {
+  uv__stream_init(loop, (uv_stream_t*)handle, UV_NAMED_PIPE);
+
+  handle->reqs_pending = 0;
+  handle->handle = INVALID_HANDLE_VALUE;
+  handle->name = NULL;
+  handle->pipe.conn.ipc_remote_pid = 0;
+  handle->pipe.conn.ipc_data_frame.payload_remaining = 0;
+  QUEUE_INIT(&handle->pipe.conn.ipc_xfer_queue);
+  handle->pipe.conn.ipc_xfer_queue_length = 0;
+  handle->ipc = ipc;
+  handle->pipe.conn.non_overlapped_writes_tail = NULL;
+
+  return 0;
+}
+
+
+static void uv__pipe_connection_init(uv_pipe_t* handle) {
+  assert(!(handle->flags & UV_HANDLE_PIPESERVER));
+  uv__connection_init((uv_stream_t*) handle);
+  handle->read_req.data = handle;
+  handle->pipe.conn.eof_timer = NULL;
+}
+
+
+static HANDLE open_named_pipe(const WCHAR* name, DWORD* duplex_flags) {
+  HANDLE pipeHandle;
+
+  /*
+   * Assume that we have a duplex pipe first, so attempt to
+   * connect with GENERIC_READ | GENERIC_WRITE.
+   */
+  pipeHandle = CreateFileW(name,
+                           GENERIC_READ | GENERIC_WRITE,
+                           0,
+                           NULL,
+                           OPEN_EXISTING,
+                           FILE_FLAG_OVERLAPPED,
+                           NULL);
+  if (pipeHandle != INVALID_HANDLE_VALUE) {
+    *duplex_flags = UV_HANDLE_READABLE | UV_HANDLE_WRITABLE;
+    return pipeHandle;
+  }
+
+  /*
+   * If the pipe is not duplex CreateFileW fails with
+   * ERROR_ACCESS_DENIED.  In that case try to connect
+   * as a read-only or write-only.
+   */
+  if (GetLastError() == ERROR_ACCESS_DENIED) {
+    pipeHandle = CreateFileW(name,
+                             GENERIC_READ | FILE_WRITE_ATTRIBUTES,
+                             0,
+                             NULL,
+                             OPEN_EXISTING,
+                             FILE_FLAG_OVERLAPPED,
+                             NULL);
+
+    if (pipeHandle != INVALID_HANDLE_VALUE) {
+      *duplex_flags = UV_HANDLE_READABLE;
+      return pipeHandle;
+    }
+  }
+
+  if (GetLastError() == ERROR_ACCESS_DENIED) {
+    pipeHandle = CreateFileW(name,
+                             GENERIC_WRITE | FILE_READ_ATTRIBUTES,
+                             0,
+                             NULL,
+                             OPEN_EXISTING,
+                             FILE_FLAG_OVERLAPPED,
+                             NULL);
+
+    if (pipeHandle != INVALID_HANDLE_VALUE) {
+      *duplex_flags = UV_HANDLE_WRITABLE;
+      return pipeHandle;
+    }
+  }
+
+  return INVALID_HANDLE_VALUE;
+}
+
+
+static void close_pipe(uv_pipe_t* pipe) {
+  assert(pipe->u.fd == -1 || pipe->u.fd > 2);
+  if (pipe->u.fd == -1)
+    CloseHandle(pipe->handle);
+  else
+    close(pipe->u.fd);
+
+  pipe->u.fd = -1;
+  pipe->handle = INVALID_HANDLE_VALUE;
+}
+
+
+static int uv__pipe_server(
+    HANDLE* pipeHandle_ptr, DWORD access,
+    char* name, size_t nameSize, char* random) {
+  HANDLE pipeHandle;
+  int err;
+
+  for (;;) {
+    uv__unique_pipe_name(random, name, nameSize);
+
+    pipeHandle = CreateNamedPipeA(name,
+      access | FILE_FLAG_FIRST_PIPE_INSTANCE,
+      PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, 1, 65536, 65536, 0,
+      NULL);
+
+    if (pipeHandle != INVALID_HANDLE_VALUE) {
+      /* No name collisions.  We're done. */
+      break;
+    }
+
+    err = GetLastError();
+    if (err != ERROR_PIPE_BUSY && err != ERROR_ACCESS_DENIED) {
+      goto error;
+    }
+
+    /* Pipe name collision.  Increment the random number and try again. */
+    random++;
+  }
+
+  *pipeHandle_ptr = pipeHandle;
+
+  return 0;
+
+ error:
+  if (pipeHandle != INVALID_HANDLE_VALUE)
+    CloseHandle(pipeHandle);
+
+  return err;
+}
+
+
+static int uv__create_pipe_pair(
+    HANDLE* server_pipe_ptr, HANDLE* client_pipe_ptr,
+    unsigned int server_flags, unsigned int client_flags,
+    int inherit_client, char* random) {
+  /* allowed flags are: UV_READABLE_PIPE | UV_WRITABLE_PIPE | UV_NONBLOCK_PIPE */
+  char pipe_name[64];
+  SECURITY_ATTRIBUTES sa;
+  DWORD server_access;
+  DWORD client_access;
+  HANDLE server_pipe;
+  HANDLE client_pipe;
+  int err;
+
+  server_pipe = INVALID_HANDLE_VALUE;
+  client_pipe = INVALID_HANDLE_VALUE;
+
+  server_access = 0;
+  if (server_flags & UV_READABLE_PIPE)
+    server_access |= PIPE_ACCESS_INBOUND;
+  if (server_flags & UV_WRITABLE_PIPE)
+    server_access |= PIPE_ACCESS_OUTBOUND;
+  if (server_flags & UV_NONBLOCK_PIPE)
+    server_access |= FILE_FLAG_OVERLAPPED;
+  server_access |= WRITE_DAC;
+
+  client_access = 0;
+  if (client_flags & UV_READABLE_PIPE)
+    client_access |= GENERIC_READ;
+  else
+    client_access |= FILE_READ_ATTRIBUTES;
+  if (client_flags & UV_WRITABLE_PIPE)
+    client_access |= GENERIC_WRITE;
+  else
+    client_access |= FILE_WRITE_ATTRIBUTES;
+  client_access |= WRITE_DAC;
+
+  /* Create server pipe handle. */
+  err = uv__pipe_server(&server_pipe,
+                        server_access,
+                        pipe_name,
+                        sizeof(pipe_name),
+                        random);
+  if (err)
+    goto error;
+
+  /* Create client pipe handle. */
+  sa.nLength = sizeof sa;
+  sa.lpSecurityDescriptor = NULL;
+  sa.bInheritHandle = inherit_client;
+
+  client_pipe = CreateFileA(pipe_name,
+                            client_access,
+                            0,
+                            &sa,
+                            OPEN_EXISTING,
+                            (client_flags & UV_NONBLOCK_PIPE) ? FILE_FLAG_OVERLAPPED : 0,
+                            NULL);
+  if (client_pipe == INVALID_HANDLE_VALUE) {
+    err = GetLastError();
+    goto error;
+  }
+
+#ifndef NDEBUG
+  /* Validate that the pipe was opened in the right mode. */
+  {
+    DWORD mode;
+    BOOL r;
+    r = GetNamedPipeHandleState(client_pipe, &mode, NULL, NULL, NULL, NULL, 0);
+    if (r == TRUE) {
+      assert(mode == (PIPE_READMODE_BYTE | PIPE_WAIT));
+    } else {
+      fprintf(stderr, "libuv assertion failure: GetNamedPipeHandleState failed\n");
+    }
+  }
+#endif
+
+  /* Do a blocking ConnectNamedPipe.  This should not block because we have
+   * both ends of the pipe created. */
+  if (!ConnectNamedPipe(server_pipe, NULL)) {
+    if (GetLastError() != ERROR_PIPE_CONNECTED) {
+      err = GetLastError();
+      goto error;
+    }
+  }
+
+  *client_pipe_ptr = client_pipe;
+  *server_pipe_ptr = server_pipe;
+  return 0;
+
+ error:
+  if (server_pipe != INVALID_HANDLE_VALUE)
+    CloseHandle(server_pipe);
+
+  if (client_pipe != INVALID_HANDLE_VALUE)
+    CloseHandle(client_pipe);
+
+  return err;
+}
+
+
+int uv_pipe(uv_file fds[2], int read_flags, int write_flags) {
+  uv_file temp[2];
+  int err;
+  HANDLE readh;
+  HANDLE writeh;
+
+  /* Make the server side the inbound (read) end, */
+  /* so that both ends will have FILE_READ_ATTRIBUTES permission. */
+  /* TODO: better source of local randomness than &fds? */
+  read_flags |= UV_READABLE_PIPE;
+  write_flags |= UV_WRITABLE_PIPE;
+  err = uv__create_pipe_pair(&readh, &writeh, read_flags, write_flags, 0, (char*) &fds[0]);
+  if (err != 0)
+    return err;
+  temp[0] = _open_osfhandle((intptr_t) readh, 0);
+  if (temp[0] == -1) {
+    if (errno == UV_EMFILE)
+      err = UV_EMFILE;
+    else
+      err = UV_UNKNOWN;
+    CloseHandle(readh);
+    CloseHandle(writeh);
+    return err;
+  }
+  temp[1] = _open_osfhandle((intptr_t) writeh, 0);
+  if (temp[1] == -1) {
+    if (errno == UV_EMFILE)
+      err = UV_EMFILE;
+    else
+      err = UV_UNKNOWN;
+    _close(temp[0]);
+    CloseHandle(writeh);
+    return err;
+  }
+  fds[0] = temp[0];
+  fds[1] = temp[1];
+  return 0;
+}
+
+
+int uv__create_stdio_pipe_pair(uv_loop_t* loop,
+    uv_pipe_t* parent_pipe, HANDLE* child_pipe_ptr, unsigned int flags) {
+  /* The parent_pipe is always the server_pipe and kept by libuv.
+   * The child_pipe is always the client_pipe and is passed to the child.
+   * The flags are specified with respect to their usage in the child. */
+  HANDLE server_pipe;
+  HANDLE client_pipe;
+  unsigned int server_flags;
+  unsigned int client_flags;
+  int err;
+
+  uv__pipe_connection_init(parent_pipe);
+
+  server_pipe = INVALID_HANDLE_VALUE;
+  client_pipe = INVALID_HANDLE_VALUE;
+
+  server_flags = 0;
+  client_flags = 0;
+  if (flags & UV_READABLE_PIPE) {
+    /* The server needs inbound (read) access too, otherwise CreateNamedPipe()
+     * won't give us the FILE_READ_ATTRIBUTES permission. We need that to probe
+     * the state of the write buffer when we're trying to shutdown the pipe. */
+    server_flags |= UV_READABLE_PIPE | UV_WRITABLE_PIPE;
+    client_flags |= UV_READABLE_PIPE;
+  }
+  if (flags & UV_WRITABLE_PIPE) {
+    server_flags |= UV_READABLE_PIPE;
+    client_flags |= UV_WRITABLE_PIPE;
+  }
+  server_flags |= UV_NONBLOCK_PIPE;
+  if (flags & UV_NONBLOCK_PIPE || parent_pipe->ipc) {
+    client_flags |= UV_NONBLOCK_PIPE;
+  }
+
+  err = uv__create_pipe_pair(&server_pipe, &client_pipe,
+          server_flags, client_flags, 1, (char*) server_pipe);
+  if (err)
+    goto error;
+
+  if (CreateIoCompletionPort(server_pipe,
+                             loop->iocp,
+                             (ULONG_PTR) parent_pipe,
+                             0) == NULL) {
+    err = GetLastError();
+    goto error;
+  }
+
+  parent_pipe->handle = server_pipe;
+  *child_pipe_ptr = client_pipe;
+
+  /* The server end is now readable and/or writable. */
+  if (flags & UV_READABLE_PIPE)
+    parent_pipe->flags |= UV_HANDLE_WRITABLE;
+  if (flags & UV_WRITABLE_PIPE)
+    parent_pipe->flags |= UV_HANDLE_READABLE;
+
+  return 0;
+
+ error:
+  if (server_pipe != INVALID_HANDLE_VALUE)
+    CloseHandle(server_pipe);
+
+  if (client_pipe != INVALID_HANDLE_VALUE)
+    CloseHandle(client_pipe);
+
+  return err;
+}
+
+
+static int uv__set_pipe_handle(uv_loop_t* loop,
+                               uv_pipe_t* handle,
+                               HANDLE pipeHandle,
+                               int fd,
+                               DWORD duplex_flags) {
+  NTSTATUS nt_status;
+  IO_STATUS_BLOCK io_status;
+  FILE_MODE_INFORMATION mode_info;
+  DWORD mode = PIPE_READMODE_BYTE | PIPE_WAIT;
+  DWORD current_mode = 0;
+  DWORD err = 0;
+
+  assert(handle->flags & UV_HANDLE_CONNECTION);
+  assert(!(handle->flags & UV_HANDLE_PIPESERVER));
+  if (handle->flags & UV_HANDLE_CLOSING)
+    return UV_EINVAL;
+  if (handle->handle != INVALID_HANDLE_VALUE)
+    return UV_EBUSY;
+
+  if (!SetNamedPipeHandleState(pipeHandle, &mode, NULL, NULL)) {
+    err = GetLastError();
+    if (err == ERROR_ACCESS_DENIED) {
+      /*
+       * SetNamedPipeHandleState can fail if the handle doesn't have either
+       * GENERIC_WRITE  or FILE_WRITE_ATTRIBUTES.
+       * But if the handle already has the desired wait and blocking modes
+       * we can continue.
+       */
+      if (!GetNamedPipeHandleState(pipeHandle, &current_mode, NULL, NULL,
+                                   NULL, NULL, 0)) {
+        return uv_translate_sys_error(GetLastError());
+      } else if (current_mode & PIPE_NOWAIT) {
+        return UV_EACCES;
+      }
+    } else {
+      /* If this returns ERROR_INVALID_PARAMETER we probably opened
+       * something that is not a pipe. */
+      if (err == ERROR_INVALID_PARAMETER) {
+        return UV_ENOTSOCK;
+      }
+      return uv_translate_sys_error(err);
+    }
+  }
+
+  /* Check if the pipe was created with FILE_FLAG_OVERLAPPED. */
+  nt_status = pNtQueryInformationFile(pipeHandle,
+                                      &io_status,
+                                      &mode_info,
+                                      sizeof(mode_info),
+                                      FileModeInformation);
+  if (nt_status != STATUS_SUCCESS) {
+    return uv_translate_sys_error(err);
+  }
+
+  if (mode_info.Mode & FILE_SYNCHRONOUS_IO_ALERT ||
+      mode_info.Mode & FILE_SYNCHRONOUS_IO_NONALERT) {
+    /* Non-overlapped pipe. */
+    handle->flags |= UV_HANDLE_NON_OVERLAPPED_PIPE;
+    handle->pipe.conn.readfile_thread_handle = NULL;
+    InitializeCriticalSection(&handle->pipe.conn.readfile_thread_lock);
+  } else {
+    /* Overlapped pipe.  Try to associate with IOCP. */
+    if (CreateIoCompletionPort(pipeHandle,
+                               loop->iocp,
+                               (ULONG_PTR) handle,
+                               0) == NULL) {
+      handle->flags |= UV_HANDLE_EMULATE_IOCP;
+    }
+  }
+
+  handle->handle = pipeHandle;
+  handle->u.fd = fd;
+  handle->flags |= duplex_flags;
+
+  return 0;
+}
+
+
+static int pipe_alloc_accept(uv_loop_t* loop, uv_pipe_t* handle,
+                             uv_pipe_accept_t* req, BOOL firstInstance) {
+  assert(req->pipeHandle == INVALID_HANDLE_VALUE);
+
+  req->pipeHandle =
+      CreateNamedPipeW(handle->name,
+                       PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED | WRITE_DAC |
+                         (firstInstance ? FILE_FLAG_FIRST_PIPE_INSTANCE : 0),
+                       PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT,
+                       PIPE_UNLIMITED_INSTANCES, 65536, 65536, 0, NULL);
+
+  if (req->pipeHandle == INVALID_HANDLE_VALUE) {
+    return 0;
+  }
+
+  /* Associate it with IOCP so we can get events. */
+  if (CreateIoCompletionPort(req->pipeHandle,
+                             loop->iocp,
+                             (ULONG_PTR) handle,
+                             0) == NULL) {
+    uv_fatal_error(GetLastError(), "CreateIoCompletionPort");
+  }
+
+  /* Stash a handle in the server object for use from places such as
+   * getsockname and chmod. As we transfer ownership of these to client
+   * objects, we'll allocate new ones here. */
+  handle->handle = req->pipeHandle;
+
+  return 1;
+}
+
+
+static DWORD WINAPI pipe_shutdown_thread_proc(void* parameter) {
+  uv_loop_t* loop;
+  uv_pipe_t* handle;
+  uv_shutdown_t* req;
+
+  req = (uv_shutdown_t*) parameter;
+  assert(req);
+  handle = (uv_pipe_t*) req->handle;
+  assert(handle);
+  loop = handle->loop;
+  assert(loop);
+
+  FlushFileBuffers(handle->handle);
+
+  /* Post completed */
+  POST_COMPLETION_FOR_REQ(loop, req);
+
+  return 0;
+}
+
+
+void uv__pipe_shutdown(uv_loop_t* loop, uv_pipe_t* handle, uv_shutdown_t *req) {
+  DWORD result;
+  NTSTATUS nt_status;
+  IO_STATUS_BLOCK io_status;
+  FILE_PIPE_LOCAL_INFORMATION pipe_info;
+
+  assert(handle->flags & UV_HANDLE_CONNECTION);
+  assert(req != NULL);
+  assert(handle->stream.conn.write_reqs_pending == 0);
+  SET_REQ_SUCCESS(req);
+
+  if (handle->flags & UV_HANDLE_CLOSING) {
+    uv__insert_pending_req(loop, (uv_req_t*) req);
+    return;
+  }
+
+  /* Try to avoid flushing the pipe buffer in the thread pool. */
+  nt_status = pNtQueryInformationFile(handle->handle,
+                                      &io_status,
+                                      &pipe_info,
+                                      sizeof pipe_info,
+                                      FilePipeLocalInformation);
+
+  if (nt_status != STATUS_SUCCESS) {
+    SET_REQ_ERROR(req, pRtlNtStatusToDosError(nt_status));
+    handle->flags |= UV_HANDLE_WRITABLE; /* Questionable. */
+    uv__insert_pending_req(loop, (uv_req_t*) req);
+    return;
+  }
+
+  if (pipe_info.OutboundQuota == pipe_info.WriteQuotaAvailable) {
+    /* Short-circuit, no need to call FlushFileBuffers:
+     * all writes have been read. */
+    uv__insert_pending_req(loop, (uv_req_t*) req);
+    return;
+  }
+
+  /* Run FlushFileBuffers in the thread pool. */
+  result = QueueUserWorkItem(pipe_shutdown_thread_proc,
+                             req,
+                             WT_EXECUTELONGFUNCTION);
+  if (!result) {
+    SET_REQ_ERROR(req, GetLastError());
+    handle->flags |= UV_HANDLE_WRITABLE; /* Questionable. */
+    uv__insert_pending_req(loop, (uv_req_t*) req);
+    return;
+  }
+}
+
+
+void uv__pipe_endgame(uv_loop_t* loop, uv_pipe_t* handle) {
+  uv__ipc_xfer_queue_item_t* xfer_queue_item;
+
+  assert(handle->reqs_pending == 0);
+  assert(handle->flags & UV_HANDLE_CLOSING);
+  assert(!(handle->flags & UV_HANDLE_CLOSED));
+
+  if (handle->flags & UV_HANDLE_CONNECTION) {
+    /* Free pending sockets */
+    while (!QUEUE_EMPTY(&handle->pipe.conn.ipc_xfer_queue)) {
+      QUEUE* q;
+      SOCKET socket;
+
+      q = QUEUE_HEAD(&handle->pipe.conn.ipc_xfer_queue);
+      QUEUE_REMOVE(q);
+      xfer_queue_item = QUEUE_DATA(q, uv__ipc_xfer_queue_item_t, member);
+
+      /* Materialize socket and close it */
+      socket = WSASocketW(FROM_PROTOCOL_INFO,
+                          FROM_PROTOCOL_INFO,
+                          FROM_PROTOCOL_INFO,
+                          &xfer_queue_item->xfer_info.socket_info,
+                          0,
+                          WSA_FLAG_OVERLAPPED);
+      uv__free(xfer_queue_item);
+
+      if (socket != INVALID_SOCKET)
+        closesocket(socket);
+    }
+    handle->pipe.conn.ipc_xfer_queue_length = 0;
+
+    if (handle->flags & UV_HANDLE_EMULATE_IOCP) {
+      if (handle->read_req.wait_handle != INVALID_HANDLE_VALUE) {
+        UnregisterWait(handle->read_req.wait_handle);
+        handle->read_req.wait_handle = INVALID_HANDLE_VALUE;
+      }
+      if (handle->read_req.event_handle != NULL) {
+        CloseHandle(handle->read_req.event_handle);
+        handle->read_req.event_handle = NULL;
+      }
+    }
+
+    if (handle->flags & UV_HANDLE_NON_OVERLAPPED_PIPE)
+      DeleteCriticalSection(&handle->pipe.conn.readfile_thread_lock);
+  }
+
+  if (handle->flags & UV_HANDLE_PIPESERVER) {
+    assert(handle->pipe.serv.accept_reqs);
+    uv__free(handle->pipe.serv.accept_reqs);
+    handle->pipe.serv.accept_reqs = NULL;
+  }
+
+  uv__handle_close(handle);
+}
+
+
+void uv_pipe_pending_instances(uv_pipe_t* handle, int count) {
+  if (handle->flags & UV_HANDLE_BOUND)
+    return;
+  handle->pipe.serv.pending_instances = count;
+  handle->flags |= UV_HANDLE_PIPESERVER;
+}
+
+
+/* Creates a pipe server. */
+int uv_pipe_bind(uv_pipe_t* handle, const char* name) {
+  uv_loop_t* loop = handle->loop;
+  int i, err, nameSize;
+  uv_pipe_accept_t* req;
+
+  if (handle->flags & UV_HANDLE_BOUND) {
+    return UV_EINVAL;
+  }
+
+  if (!name) {
+    return UV_EINVAL;
+  }
+  if (uv__is_closing(handle)) {
+    return UV_EINVAL;
+  }
+  if (!(handle->flags & UV_HANDLE_PIPESERVER)) {
+    handle->pipe.serv.pending_instances = default_pending_pipe_instances;
+  }
+
+  handle->pipe.serv.accept_reqs = (uv_pipe_accept_t*)
+    uv__malloc(sizeof(uv_pipe_accept_t) * handle->pipe.serv.pending_instances);
+  if (!handle->pipe.serv.accept_reqs) {
+    uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc");
+  }
+
+  for (i = 0; i < handle->pipe.serv.pending_instances; i++) {
+    req = &handle->pipe.serv.accept_reqs[i];
+    UV_REQ_INIT(req, UV_ACCEPT);
+    req->data = handle;
+    req->pipeHandle = INVALID_HANDLE_VALUE;
+    req->next_pending = NULL;
+  }
+
+  /* Convert name to UTF16. */
+  nameSize = MultiByteToWideChar(CP_UTF8, 0, name, -1, NULL, 0) * sizeof(WCHAR);
+  handle->name = (WCHAR*)uv__malloc(nameSize);
+  if (!handle->name) {
+    uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc");
+  }
+
+  if (!MultiByteToWideChar(CP_UTF8,
+                           0,
+                           name,
+                           -1,
+                           handle->name,
+                           nameSize / sizeof(WCHAR))) {
+    err = GetLastError();
+    goto error;
+  }
+
+  /*
+   * Attempt to create the first pipe with FILE_FLAG_FIRST_PIPE_INSTANCE.
+   * If this fails then there's already a pipe server for the given pipe name.
+   */
+  if (!pipe_alloc_accept(loop,
+                         handle,
+                         &handle->pipe.serv.accept_reqs[0],
+                         TRUE)) {
+    err = GetLastError();
+    if (err == ERROR_ACCESS_DENIED) {
+      err = WSAEADDRINUSE;  /* Translates to UV_EADDRINUSE. */
+    } else if (err == ERROR_PATH_NOT_FOUND || err == ERROR_INVALID_NAME) {
+      err = WSAEACCES;  /* Translates to UV_EACCES. */
+    }
+    goto error;
+  }
+
+  handle->pipe.serv.pending_accepts = NULL;
+  handle->flags |= UV_HANDLE_PIPESERVER;
+  handle->flags |= UV_HANDLE_BOUND;
+
+  return 0;
+
+error:
+  if (handle->name) {
+    uv__free(handle->name);
+    handle->name = NULL;
+  }
+
+  return uv_translate_sys_error(err);
+}
+
+
+static DWORD WINAPI pipe_connect_thread_proc(void* parameter) {
+  uv_loop_t* loop;
+  uv_pipe_t* handle;
+  uv_connect_t* req;
+  HANDLE pipeHandle = INVALID_HANDLE_VALUE;
+  DWORD duplex_flags;
+
+  req = (uv_connect_t*) parameter;
+  assert(req);
+  handle = (uv_pipe_t*) req->handle;
+  assert(handle);
+  loop = handle->loop;
+  assert(loop);
+
+  /* We're here because CreateFile on a pipe returned ERROR_PIPE_BUSY. We wait
+   * up to 30 seconds for the pipe to become available with WaitNamedPipe. */
+  while (WaitNamedPipeW(handle->name, 30000)) {
+    /* The pipe is now available, try to connect. */
+    pipeHandle = open_named_pipe(handle->name, &duplex_flags);
+    if (pipeHandle != INVALID_HANDLE_VALUE)
+      break;
+
+    SwitchToThread();
+  }
+
+  if (pipeHandle != INVALID_HANDLE_VALUE) {
+    SET_REQ_SUCCESS(req);
+    req->u.connect.pipeHandle = pipeHandle;
+    req->u.connect.duplex_flags = duplex_flags;
+  } else {
+    SET_REQ_ERROR(req, GetLastError());
+  }
+
+  /* Post completed */
+  POST_COMPLETION_FOR_REQ(loop, req);
+
+  return 0;
+}
+
+
+void uv_pipe_connect(uv_connect_t* req, uv_pipe_t* handle,
+    const char* name, uv_connect_cb cb) {
+  uv_loop_t* loop = handle->loop;
+  int err, nameSize;
+  HANDLE pipeHandle = INVALID_HANDLE_VALUE;
+  DWORD duplex_flags;
+
+  UV_REQ_INIT(req, UV_CONNECT);
+  req->handle = (uv_stream_t*) handle;
+  req->cb = cb;
+  req->u.connect.pipeHandle = INVALID_HANDLE_VALUE;
+  req->u.connect.duplex_flags = 0;
+
+  if (handle->flags & UV_HANDLE_PIPESERVER) {
+    err = ERROR_INVALID_PARAMETER;
+    goto error;
+  }
+  if (handle->flags & UV_HANDLE_CONNECTION) {
+    err = ERROR_PIPE_BUSY;
+    goto error;
+  }
+  uv__pipe_connection_init(handle);
+
+  /* Convert name to UTF16. */
+  nameSize = MultiByteToWideChar(CP_UTF8, 0, name, -1, NULL, 0) * sizeof(WCHAR);
+  handle->name = (WCHAR*)uv__malloc(nameSize);
+  if (!handle->name) {
+    uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc");
+  }
+
+  if (!MultiByteToWideChar(CP_UTF8,
+                           0,
+                           name,
+                           -1,
+                           handle->name,
+                           nameSize / sizeof(WCHAR))) {
+    err = GetLastError();
+    goto error;
+  }
+
+  pipeHandle = open_named_pipe(handle->name, &duplex_flags);
+  if (pipeHandle == INVALID_HANDLE_VALUE) {
+    if (GetLastError() == ERROR_PIPE_BUSY) {
+      /* Wait for the server to make a pipe instance available. */
+      if (!QueueUserWorkItem(&pipe_connect_thread_proc,
+                             req,
+                             WT_EXECUTELONGFUNCTION)) {
+        err = GetLastError();
+        goto error;
+      }
+
+      REGISTER_HANDLE_REQ(loop, handle, req);
+      handle->reqs_pending++;
+
+      return;
+    }
+
+    err = GetLastError();
+    goto error;
+  }
+
+  req->u.connect.pipeHandle = pipeHandle;
+  req->u.connect.duplex_flags = duplex_flags;
+  SET_REQ_SUCCESS(req);
+  uv__insert_pending_req(loop, (uv_req_t*) req);
+  handle->reqs_pending++;
+  REGISTER_HANDLE_REQ(loop, handle, req);
+  return;
+
+error:
+  if (handle->name) {
+    uv__free(handle->name);
+    handle->name = NULL;
+  }
+
+  if (pipeHandle != INVALID_HANDLE_VALUE)
+    CloseHandle(pipeHandle);
+
+  /* Make this req pending reporting an error. */
+  SET_REQ_ERROR(req, err);
+  uv__insert_pending_req(loop, (uv_req_t*) req);
+  handle->reqs_pending++;
+  REGISTER_HANDLE_REQ(loop, handle, req);
+  return;
+}
+
+
+void uv__pipe_interrupt_read(uv_pipe_t* handle) {
+  BOOL r;
+
+  if (!(handle->flags & UV_HANDLE_READ_PENDING))
+    return; /* No pending reads. */
+  if (handle->flags & UV_HANDLE_CANCELLATION_PENDING)
+    return; /* Already cancelled. */
+  if (handle->handle == INVALID_HANDLE_VALUE)
+    return; /* Pipe handle closed. */
+
+  if (!(handle->flags & UV_HANDLE_NON_OVERLAPPED_PIPE)) {
+    /* Cancel asynchronous read. */
+    r = CancelIoEx(handle->handle, &handle->read_req.u.io.overlapped);
+    assert(r || GetLastError() == ERROR_NOT_FOUND);
+    (void) r;
+  } else {
+    /* Cancel synchronous read (which is happening in the thread pool). */
+    HANDLE thread;
+    volatile HANDLE* thread_ptr = &handle->pipe.conn.readfile_thread_handle;
+
+    EnterCriticalSection(&handle->pipe.conn.readfile_thread_lock);
+
+    thread = *thread_ptr;
+    if (thread == NULL) {
+      /* The thread pool thread has not yet reached the point of blocking, we
+       * can pre-empt it by setting thread_handle to INVALID_HANDLE_VALUE. */
+      *thread_ptr = INVALID_HANDLE_VALUE;
+
+    } else {
+      /* Spin until the thread has acknowledged (by setting the thread to
+       * INVALID_HANDLE_VALUE) that it is past the point of blocking. */
+      while (thread != INVALID_HANDLE_VALUE) {
+        r = CancelSynchronousIo(thread);
+        assert(r || GetLastError() == ERROR_NOT_FOUND);
+        SwitchToThread(); /* Yield thread. */
+        thread = *thread_ptr;
+      }
+    }
+
+    LeaveCriticalSection(&handle->pipe.conn.readfile_thread_lock);
+  }
+
+  /* Set flag to indicate that read has been cancelled. */
+  handle->flags |= UV_HANDLE_CANCELLATION_PENDING;
+}
+
+
+void uv__pipe_read_stop(uv_pipe_t* handle) {
+  handle->flags &= ~UV_HANDLE_READING;
+  DECREASE_ACTIVE_COUNT(handle->loop, handle);
+  uv__pipe_interrupt_read(handle);
+}
+
+
+/* Cleans up uv_pipe_t (server or connection) and all resources associated with
+ * it. */
+void uv__pipe_close(uv_loop_t* loop, uv_pipe_t* handle) {
+  int i;
+  HANDLE pipeHandle;
+
+  if (handle->flags & UV_HANDLE_READING) {
+    handle->flags &= ~UV_HANDLE_READING;
+    DECREASE_ACTIVE_COUNT(loop, handle);
+  }
+
+  if (handle->flags & UV_HANDLE_LISTENING) {
+    handle->flags &= ~UV_HANDLE_LISTENING;
+    DECREASE_ACTIVE_COUNT(loop, handle);
+  }
+
+  handle->flags &= ~(UV_HANDLE_READABLE | UV_HANDLE_WRITABLE);
+
+  uv__handle_closing(handle);
+
+  uv__pipe_interrupt_read(handle);
+
+  if (handle->name) {
+    uv__free(handle->name);
+    handle->name = NULL;
+  }
+
+  if (handle->flags & UV_HANDLE_PIPESERVER) {
+    for (i = 0; i < handle->pipe.serv.pending_instances; i++) {
+      pipeHandle = handle->pipe.serv.accept_reqs[i].pipeHandle;
+      if (pipeHandle != INVALID_HANDLE_VALUE) {
+        CloseHandle(pipeHandle);
+        handle->pipe.serv.accept_reqs[i].pipeHandle = INVALID_HANDLE_VALUE;
+      }
+    }
+    handle->handle = INVALID_HANDLE_VALUE;
+  }
+
+  if (handle->flags & UV_HANDLE_CONNECTION) {
+    eof_timer_destroy(handle);
+  }
+
+  if ((handle->flags & UV_HANDLE_CONNECTION)
+      && handle->handle != INVALID_HANDLE_VALUE) {
+    /* This will eventually destroy the write queue for us too. */
+    close_pipe(handle);
+  }
+
+  if (handle->reqs_pending == 0)
+    uv__want_endgame(loop, (uv_handle_t*) handle);
+}
+
+
+static void uv__pipe_queue_accept(uv_loop_t* loop, uv_pipe_t* handle,
+    uv_pipe_accept_t* req, BOOL firstInstance) {
+  assert(handle->flags & UV_HANDLE_LISTENING);
+
+  if (!firstInstance && !pipe_alloc_accept(loop, handle, req, FALSE)) {
+    SET_REQ_ERROR(req, GetLastError());
+    uv__insert_pending_req(loop, (uv_req_t*) req);
+    handle->reqs_pending++;
+    return;
+  }
+
+  assert(req->pipeHandle != INVALID_HANDLE_VALUE);
+
+  /* Prepare the overlapped structure. */
+  memset(&(req->u.io.overlapped), 0, sizeof(req->u.io.overlapped));
+
+  if (!ConnectNamedPipe(req->pipeHandle, &req->u.io.overlapped) &&
+      GetLastError() != ERROR_IO_PENDING) {
+    if (GetLastError() == ERROR_PIPE_CONNECTED) {
+      SET_REQ_SUCCESS(req);
+    } else {
+      CloseHandle(req->pipeHandle);
+      req->pipeHandle = INVALID_HANDLE_VALUE;
+      /* Make this req pending reporting an error. */
+      SET_REQ_ERROR(req, GetLastError());
+    }
+    uv__insert_pending_req(loop, (uv_req_t*) req);
+    handle->reqs_pending++;
+    return;
+  }
+
+  /* Wait for completion via IOCP */
+  handle->reqs_pending++;
+}
+
+
+int uv__pipe_accept(uv_pipe_t* server, uv_stream_t* client) {
+  uv_loop_t* loop = server->loop;
+  uv_pipe_t* pipe_client;
+  uv_pipe_accept_t* req;
+  QUEUE* q;
+  uv__ipc_xfer_queue_item_t* item;
+  int err;
+
+  if (server->ipc) {
+    if (QUEUE_EMPTY(&server->pipe.conn.ipc_xfer_queue)) {
+      /* No valid pending sockets. */
+      return WSAEWOULDBLOCK;
+    }
+
+    q = QUEUE_HEAD(&server->pipe.conn.ipc_xfer_queue);
+    QUEUE_REMOVE(q);
+    server->pipe.conn.ipc_xfer_queue_length--;
+    item = QUEUE_DATA(q, uv__ipc_xfer_queue_item_t, member);
+
+    err = uv__tcp_xfer_import(
+        (uv_tcp_t*) client, item->xfer_type, &item->xfer_info);
+    if (err != 0)
+      return err;
+
+    uv__free(item);
+
+  } else {
+    pipe_client = (uv_pipe_t*) client;
+    uv__pipe_connection_init(pipe_client);
+
+    /* Find a connection instance that has been connected, but not yet
+     * accepted. */
+    req = server->pipe.serv.pending_accepts;
+
+    if (!req) {
+      /* No valid connections found, so we error out. */
+      return WSAEWOULDBLOCK;
+    }
+
+    /* Initialize the client handle and copy the pipeHandle to the client */
+    pipe_client->handle = req->pipeHandle;
+    pipe_client->flags |= UV_HANDLE_READABLE | UV_HANDLE_WRITABLE;
+
+    /* Prepare the req to pick up a new connection */
+    server->pipe.serv.pending_accepts = req->next_pending;
+    req->next_pending = NULL;
+    req->pipeHandle = INVALID_HANDLE_VALUE;
+
+    server->handle = INVALID_HANDLE_VALUE;
+    if (!(server->flags & UV_HANDLE_CLOSING)) {
+      uv__pipe_queue_accept(loop, server, req, FALSE);
+    }
+  }
+
+  return 0;
+}
+
+
+/* Starts listening for connections for the given pipe. */
+int uv__pipe_listen(uv_pipe_t* handle, int backlog, uv_connection_cb cb) {
+  uv_loop_t* loop = handle->loop;
+  int i;
+
+  if (handle->flags & UV_HANDLE_LISTENING) {
+    handle->stream.serv.connection_cb = cb;
+  }
+
+  if (!(handle->flags & UV_HANDLE_BOUND)) {
+    return WSAEINVAL;
+  }
+
+  if (handle->flags & UV_HANDLE_READING) {
+    return WSAEISCONN;
+  }
+
+  if (!(handle->flags & UV_HANDLE_PIPESERVER)) {
+    return ERROR_NOT_SUPPORTED;
+  }
+
+  if (handle->ipc) {
+    return WSAEINVAL;
+  }
+
+  handle->flags |= UV_HANDLE_LISTENING;
+  INCREASE_ACTIVE_COUNT(loop, handle);
+  handle->stream.serv.connection_cb = cb;
+
+  /* First pipe handle should have already been created in uv_pipe_bind */
+  assert(handle->pipe.serv.accept_reqs[0].pipeHandle != INVALID_HANDLE_VALUE);
+
+  for (i = 0; i < handle->pipe.serv.pending_instances; i++) {
+    uv__pipe_queue_accept(loop, handle, &handle->pipe.serv.accept_reqs[i], i == 0);
+  }
+
+  return 0;
+}
+
+
+static DWORD WINAPI uv_pipe_zero_readfile_thread_proc(void* arg) {
+  uv_read_t* req = (uv_read_t*) arg;
+  uv_pipe_t* handle = (uv_pipe_t*) req->data;
+  uv_loop_t* loop = handle->loop;
+  volatile HANDLE* thread_ptr = &handle->pipe.conn.readfile_thread_handle;
+  CRITICAL_SECTION* lock = &handle->pipe.conn.readfile_thread_lock;
+  HANDLE thread;
+  DWORD bytes;
+  DWORD err;
+
+  assert(req->type == UV_READ);
+  assert(handle->type == UV_NAMED_PIPE);
+
+  err = 0;
+
+  /* Create a handle to the current thread. */
+  if (!DuplicateHandle(GetCurrentProcess(),
+                       GetCurrentThread(),
+                       GetCurrentProcess(),
+                       &thread,
+                       0,
+                       FALSE,
+                       DUPLICATE_SAME_ACCESS)) {
+    err = GetLastError();
+    goto out1;
+  }
+
+  /* The lock needs to be held when thread handle is modified. */
+  EnterCriticalSection(lock);
+  if (*thread_ptr == INVALID_HANDLE_VALUE) {
+    /* uv__pipe_interrupt_read() cancelled reading before we got here. */
+    err = ERROR_OPERATION_ABORTED;
+  } else {
+    /* Let main thread know which worker thread is doing the blocking read. */
+    assert(*thread_ptr == NULL);
+    *thread_ptr = thread;
+  }
+  LeaveCriticalSection(lock);
+
+  if (err)
+    goto out2;
+
+  /* Block the thread until data is available on the pipe, or the read is
+   * cancelled. */
+  if (!ReadFile(handle->handle, &uv_zero_, 0, &bytes, NULL))
+    err = GetLastError();
+
+  /* Let the main thread know the worker is past the point of blocking. */
+  assert(thread == *thread_ptr);
+  *thread_ptr = INVALID_HANDLE_VALUE;
+
+  /* Briefly acquire the mutex. Since the main thread holds the lock while it
+   * is spinning trying to cancel this thread's I/O, we will block here until
+   * it stops doing that. */
+  EnterCriticalSection(lock);
+  LeaveCriticalSection(lock);
+
+out2:
+  /* Close the handle to the current thread. */
+  CloseHandle(thread);
+
+out1:
+  /* Set request status and post a completion record to the IOCP. */
+  if (err)
+    SET_REQ_ERROR(req, err);
+  else
+    SET_REQ_SUCCESS(req);
+  POST_COMPLETION_FOR_REQ(loop, req);
+
+  return 0;
+}
+
+
+static DWORD WINAPI uv_pipe_writefile_thread_proc(void* parameter) {
+  int result;
+  DWORD bytes;
+  uv_write_t* req = (uv_write_t*) parameter;
+  uv_pipe_t* handle = (uv_pipe_t*) req->handle;
+  uv_loop_t* loop = handle->loop;
+
+  assert(req != NULL);
+  assert(req->type == UV_WRITE);
+  assert(handle->type == UV_NAMED_PIPE);
+
+  result = WriteFile(handle->handle,
+                     req->write_buffer.base,
+                     req->write_buffer.len,
+                     &bytes,
+                     NULL);
+
+  if (!result) {
+    SET_REQ_ERROR(req, GetLastError());
+  }
+
+  POST_COMPLETION_FOR_REQ(loop, req);
+  return 0;
+}
+
+
+static void CALLBACK post_completion_read_wait(void* context, BOOLEAN timed_out) {
+  uv_read_t* req;
+  uv_tcp_t* handle;
+
+  req = (uv_read_t*) context;
+  assert(req != NULL);
+  handle = (uv_tcp_t*)req->data;
+  assert(handle != NULL);
+  assert(!timed_out);
+
+  if (!PostQueuedCompletionStatus(handle->loop->iocp,
+                                  req->u.io.overlapped.InternalHigh,
+                                  0,
+                                  &req->u.io.overlapped)) {
+    uv_fatal_error(GetLastError(), "PostQueuedCompletionStatus");
+  }
+}
+
+
+static void CALLBACK post_completion_write_wait(void* context, BOOLEAN timed_out) {
+  uv_write_t* req;
+  uv_tcp_t* handle;
+
+  req = (uv_write_t*) context;
+  assert(req != NULL);
+  handle = (uv_tcp_t*)req->handle;
+  assert(handle != NULL);
+  assert(!timed_out);
+
+  if (!PostQueuedCompletionStatus(handle->loop->iocp,
+                                  req->u.io.overlapped.InternalHigh,
+                                  0,
+                                  &req->u.io.overlapped)) {
+    uv_fatal_error(GetLastError(), "PostQueuedCompletionStatus");
+  }
+}
+
+
+static void uv__pipe_queue_read(uv_loop_t* loop, uv_pipe_t* handle) {
+  uv_read_t* req;
+  int result;
+
+  assert(handle->flags & UV_HANDLE_READING);
+  assert(!(handle->flags & UV_HANDLE_READ_PENDING));
+
+  assert(handle->handle != INVALID_HANDLE_VALUE);
+
+  req = &handle->read_req;
+
+  if (handle->flags & UV_HANDLE_NON_OVERLAPPED_PIPE) {
+    handle->pipe.conn.readfile_thread_handle = NULL; /* Reset cancellation. */
+    if (!QueueUserWorkItem(&uv_pipe_zero_readfile_thread_proc,
+                           req,
+                           WT_EXECUTELONGFUNCTION)) {
+      /* Make this req pending reporting an error. */
+      SET_REQ_ERROR(req, GetLastError());
+      goto error;
+    }
+  } else {
+    memset(&req->u.io.overlapped, 0, sizeof(req->u.io.overlapped));
+    if (handle->flags & UV_HANDLE_EMULATE_IOCP) {
+      assert(req->event_handle != NULL);
+      req->u.io.overlapped.hEvent = (HANDLE) ((uintptr_t) req->event_handle | 1);
+    }
+
+    /* Do 0-read */
+    result = ReadFile(handle->handle,
+                      &uv_zero_,
+                      0,
+                      NULL,
+                      &req->u.io.overlapped);
+
+    if (!result && GetLastError() != ERROR_IO_PENDING) {
+      /* Make this req pending reporting an error. */
+      SET_REQ_ERROR(req, GetLastError());
+      goto error;
+    }
+
+    if (handle->flags & UV_HANDLE_EMULATE_IOCP) {
+      if (req->wait_handle == INVALID_HANDLE_VALUE) {
+        if (!RegisterWaitForSingleObject(&req->wait_handle,
+            req->event_handle, post_completion_read_wait, (void*) req,
+            INFINITE, WT_EXECUTEINWAITTHREAD)) {
+          SET_REQ_ERROR(req, GetLastError());
+          goto error;
+        }
+      }
+    }
+  }
+
+  /* Start the eof timer if there is one */
+  eof_timer_start(handle);
+  handle->flags |= UV_HANDLE_READ_PENDING;
+  handle->reqs_pending++;
+  return;
+
+error:
+  uv__insert_pending_req(loop, (uv_req_t*)req);
+  handle->flags |= UV_HANDLE_READ_PENDING;
+  handle->reqs_pending++;
+}
+
+
+int uv__pipe_read_start(uv_pipe_t* handle,
+                        uv_alloc_cb alloc_cb,
+                        uv_read_cb read_cb) {
+  uv_loop_t* loop = handle->loop;
+
+  handle->flags |= UV_HANDLE_READING;
+  INCREASE_ACTIVE_COUNT(loop, handle);
+  handle->read_cb = read_cb;
+  handle->alloc_cb = alloc_cb;
+
+  /* If reading was stopped and then started again, there could still be a read
+   * request pending. */
+  if (!(handle->flags & UV_HANDLE_READ_PENDING)) {
+    if (handle->flags & UV_HANDLE_EMULATE_IOCP &&
+        handle->read_req.event_handle == NULL) {
+      handle->read_req.event_handle = CreateEvent(NULL, 0, 0, NULL);
+      if (handle->read_req.event_handle == NULL) {
+        uv_fatal_error(GetLastError(), "CreateEvent");
+      }
+    }
+    uv__pipe_queue_read(loop, handle);
+  }
+
+  return 0;
+}
+
+
+static void uv__insert_non_overlapped_write_req(uv_pipe_t* handle,
+    uv_write_t* req) {
+  req->next_req = NULL;
+  if (handle->pipe.conn.non_overlapped_writes_tail) {
+    req->next_req =
+      handle->pipe.conn.non_overlapped_writes_tail->next_req;
+    handle->pipe.conn.non_overlapped_writes_tail->next_req = (uv_req_t*)req;
+    handle->pipe.conn.non_overlapped_writes_tail = req;
+  } else {
+    req->next_req = (uv_req_t*)req;
+    handle->pipe.conn.non_overlapped_writes_tail = req;
+  }
+}
+
+
+static uv_write_t* uv_remove_non_overlapped_write_req(uv_pipe_t* handle) {
+  uv_write_t* req;
+
+  if (handle->pipe.conn.non_overlapped_writes_tail) {
+    req = (uv_write_t*)handle->pipe.conn.non_overlapped_writes_tail->next_req;
+
+    if (req == handle->pipe.conn.non_overlapped_writes_tail) {
+      handle->pipe.conn.non_overlapped_writes_tail = NULL;
+    } else {
+      handle->pipe.conn.non_overlapped_writes_tail->next_req =
+        req->next_req;
+    }
+
+    return req;
+  } else {
+    /* queue empty */
+    return NULL;
+  }
+}
+
+
+static void uv__queue_non_overlapped_write(uv_pipe_t* handle) {
+  uv_write_t* req = uv_remove_non_overlapped_write_req(handle);
+  if (req) {
+    if (!QueueUserWorkItem(&uv_pipe_writefile_thread_proc,
+                           req,
+                           WT_EXECUTELONGFUNCTION)) {
+      uv_fatal_error(GetLastError(), "QueueUserWorkItem");
+    }
+  }
+}
+
+
+static int uv__build_coalesced_write_req(uv_write_t* user_req,
+                                         const uv_buf_t bufs[],
+                                         size_t nbufs,
+                                         uv_write_t** req_out,
+                                         uv_buf_t* write_buf_out) {
+  /* Pack into a single heap-allocated buffer:
+   *   (a) a uv_write_t structure where libuv stores the actual state.
+   *   (b) a pointer to the original uv_write_t.
+   *   (c) data from all `bufs` entries.
+   */
+  char* heap_buffer;
+  size_t heap_buffer_length, heap_buffer_offset;
+  uv__coalesced_write_t* coalesced_write_req; /* (a) + (b) */
+  char* data_start;                           /* (c) */
+  size_t data_length;
+  unsigned int i;
+
+  /* Compute combined size of all combined buffers from `bufs`. */
+  data_length = 0;
+  for (i = 0; i < nbufs; i++)
+    data_length += bufs[i].len;
+
+  /* The total combined size of data buffers should not exceed UINT32_MAX,
+   * because WriteFile() won't accept buffers larger than that. */
+  if (data_length > UINT32_MAX)
+    return WSAENOBUFS; /* Maps to UV_ENOBUFS. */
+
+  /* Compute heap buffer size. */
+  heap_buffer_length = sizeof *coalesced_write_req + /* (a) + (b) */
+                       data_length;                  /* (c) */
+
+  /* Allocate buffer. */
+  heap_buffer = (char*)uv__malloc(heap_buffer_length);
+  if (heap_buffer == NULL)
+    return ERROR_NOT_ENOUGH_MEMORY; /* Maps to UV_ENOMEM. */
+
+  /* Copy uv_write_t information to the buffer. */
+  coalesced_write_req = (uv__coalesced_write_t*) heap_buffer;
+  coalesced_write_req->req = *user_req; /* copy (a) */
+  coalesced_write_req->req.coalesced = 1;
+  coalesced_write_req->user_req = user_req;         /* copy (b) */
+  heap_buffer_offset = sizeof *coalesced_write_req; /* offset (a) + (b) */
+
+  /* Copy data buffers to the heap buffer. */
+  data_start = &heap_buffer[heap_buffer_offset];
+  for (i = 0; i < nbufs; i++) {
+    memcpy(&heap_buffer[heap_buffer_offset],
+           bufs[i].base,
+           bufs[i].len);               /* copy (c) */
+    heap_buffer_offset += bufs[i].len; /* offset (c) */
+  }
+  assert(heap_buffer_offset == heap_buffer_length);
+
+  /* Set out arguments and return. */
+  *req_out = &coalesced_write_req->req;
+  *write_buf_out = uv_buf_init(data_start, (unsigned int) data_length);
+  return 0;
+}
+
+
+static int uv__pipe_write_data(uv_loop_t* loop,
+                               uv_write_t* req,
+                               uv_pipe_t* handle,
+                               const uv_buf_t bufs[],
+                               size_t nbufs,
+                               uv_write_cb cb,
+                               int copy_always) {
+  int err;
+  int result;
+  uv_buf_t write_buf;
+
+  assert(handle->handle != INVALID_HANDLE_VALUE);
+
+  UV_REQ_INIT(req, UV_WRITE);
+  req->handle = (uv_stream_t*) handle;
+  req->send_handle = NULL;
+  req->cb = cb;
+  /* Private fields. */
+  req->coalesced = 0;
+  req->event_handle = NULL;
+  req->wait_handle = INVALID_HANDLE_VALUE;
+
+  /* Prepare the overlapped structure. */
+  memset(&req->u.io.overlapped, 0, sizeof(req->u.io.overlapped));
+  if (handle->flags & (UV_HANDLE_EMULATE_IOCP | UV_HANDLE_BLOCKING_WRITES)) {
+    req->event_handle = CreateEvent(NULL, 0, 0, NULL);
+    if (req->event_handle == NULL) {
+      uv_fatal_error(GetLastError(), "CreateEvent");
+    }
+    req->u.io.overlapped.hEvent = (HANDLE) ((uintptr_t) req->event_handle | 1);
+  }
+  req->write_buffer = uv_null_buf_;
+
+  if (nbufs == 0) {
+    /* Write empty buffer. */
+    write_buf = uv_null_buf_;
+  } else if (nbufs == 1 && !copy_always) {
+    /* Write directly from bufs[0]. */
+    write_buf = bufs[0];
+  } else {
+    /* Coalesce all `bufs` into one big buffer. This also creates a new
+     * write-request structure that replaces the old one. */
+    err = uv__build_coalesced_write_req(req, bufs, nbufs, &req, &write_buf);
+    if (err != 0)
+      return err;
+  }
+
+  if ((handle->flags &
+      (UV_HANDLE_BLOCKING_WRITES | UV_HANDLE_NON_OVERLAPPED_PIPE)) ==
+      (UV_HANDLE_BLOCKING_WRITES | UV_HANDLE_NON_OVERLAPPED_PIPE)) {
+    DWORD bytes;
+    result =
+        WriteFile(handle->handle, write_buf.base, write_buf.len, &bytes, NULL);
+
+    if (!result) {
+      err = GetLastError();
+      return err;
+    } else {
+      /* Request completed immediately. */
+      req->u.io.queued_bytes = 0;
+    }
+
+    REGISTER_HANDLE_REQ(loop, handle, req);
+    handle->reqs_pending++;
+    handle->stream.conn.write_reqs_pending++;
+    POST_COMPLETION_FOR_REQ(loop, req);
+    return 0;
+  } else if (handle->flags & UV_HANDLE_NON_OVERLAPPED_PIPE) {
+    req->write_buffer = write_buf;
+    uv__insert_non_overlapped_write_req(handle, req);
+    if (handle->stream.conn.write_reqs_pending == 0) {
+      uv__queue_non_overlapped_write(handle);
+    }
+
+    /* Request queued by the kernel. */
+    req->u.io.queued_bytes = write_buf.len;
+    handle->write_queue_size += req->u.io.queued_bytes;
+  } else if (handle->flags & UV_HANDLE_BLOCKING_WRITES) {
+    /* Using overlapped IO, but wait for completion before returning */
+    result = WriteFile(handle->handle,
+                       write_buf.base,
+                       write_buf.len,
+                       NULL,
+                       &req->u.io.overlapped);
+
+    if (!result && GetLastError() != ERROR_IO_PENDING) {
+      err = GetLastError();
+      CloseHandle(req->event_handle);
+      req->event_handle = NULL;
+      return err;
+    }
+
+    if (result) {
+      /* Request completed immediately. */
+      req->u.io.queued_bytes = 0;
+    } else {
+      /* Request queued by the kernel. */
+      req->u.io.queued_bytes = write_buf.len;
+      handle->write_queue_size += req->u.io.queued_bytes;
+      if (WaitForSingleObject(req->event_handle, INFINITE) !=
+          WAIT_OBJECT_0) {
+        err = GetLastError();
+        CloseHandle(req->event_handle);
+        req->event_handle = NULL;
+        return err;
+      }
+    }
+    CloseHandle(req->event_handle);
+    req->event_handle = NULL;
+
+    REGISTER_HANDLE_REQ(loop, handle, req);
+    handle->reqs_pending++;
+    handle->stream.conn.write_reqs_pending++;
+    return 0;
+  } else {
+    result = WriteFile(handle->handle,
+                       write_buf.base,
+                       write_buf.len,
+                       NULL,
+                       &req->u.io.overlapped);
+
+    if (!result && GetLastError() != ERROR_IO_PENDING) {
+      return GetLastError();
+    }
+
+    if (result) {
+      /* Request completed immediately. */
+      req->u.io.queued_bytes = 0;
+    } else {
+      /* Request queued by the kernel. */
+      req->u.io.queued_bytes = write_buf.len;
+      handle->write_queue_size += req->u.io.queued_bytes;
+    }
+
+    if (handle->flags & UV_HANDLE_EMULATE_IOCP) {
+      if (!RegisterWaitForSingleObject(&req->wait_handle,
+          req->event_handle, post_completion_write_wait, (void*) req,
+          INFINITE, WT_EXECUTEINWAITTHREAD)) {
+        return GetLastError();
+      }
+    }
+  }
+
+  REGISTER_HANDLE_REQ(loop, handle, req);
+  handle->reqs_pending++;
+  handle->stream.conn.write_reqs_pending++;
+
+  return 0;
+}
+
+
+static DWORD uv__pipe_get_ipc_remote_pid(uv_pipe_t* handle) {
+  DWORD* pid = &handle->pipe.conn.ipc_remote_pid;
+
+  /* If the both ends of the IPC pipe are owned by the same process,
+   * the remote end pid may not yet be set. If so, do it here.
+   * TODO: this is weird; it'd probably better to use a handshake. */
+  if (*pid == 0)
+    *pid = GetCurrentProcessId();
+
+  return *pid;
+}
+
+
+int uv__pipe_write_ipc(uv_loop_t* loop,
+                       uv_write_t* req,
+                       uv_pipe_t* handle,
+                       const uv_buf_t data_bufs[],
+                       size_t data_buf_count,
+                       uv_stream_t* send_handle,
+                       uv_write_cb cb) {
+  uv_buf_t stack_bufs[6];
+  uv_buf_t* bufs;
+  size_t buf_count, buf_index;
+  uv__ipc_frame_header_t frame_header;
+  uv__ipc_socket_xfer_type_t xfer_type = UV__IPC_SOCKET_XFER_NONE;
+  uv__ipc_socket_xfer_info_t xfer_info;
+  uint64_t data_length;
+  size_t i;
+  int err;
+
+  /* Compute the combined size of data buffers. */
+  data_length = 0;
+  for (i = 0; i < data_buf_count; i++)
+    data_length += data_bufs[i].len;
+  if (data_length > UINT32_MAX)
+    return WSAENOBUFS; /* Maps to UV_ENOBUFS. */
+
+  /* Prepare the frame's socket xfer payload. */
+  if (send_handle != NULL) {
+    uv_tcp_t* send_tcp_handle = (uv_tcp_t*) send_handle;
+
+    /* Verify that `send_handle` it is indeed a tcp handle. */
+    if (send_tcp_handle->type != UV_TCP)
+      return ERROR_NOT_SUPPORTED;
+
+    /* Export the tcp handle. */
+    err = uv__tcp_xfer_export(send_tcp_handle,
+                              uv__pipe_get_ipc_remote_pid(handle),
+                              &xfer_type,
+                              &xfer_info);
+    if (err != 0)
+      return err;
+  }
+
+  /* Compute the number of uv_buf_t's required. */
+  buf_count = 1 + data_buf_count; /* Frame header and data buffers. */
+  if (send_handle != NULL)
+    buf_count += 1; /* One extra for the socket xfer information. */
+
+  /* Use the on-stack buffer array if it is big enough; otherwise allocate
+   * space for it on the heap. */
+  if (buf_count < ARRAY_SIZE(stack_bufs)) {
+    /* Use on-stack buffer array. */
+    bufs = stack_bufs;
+  } else {
+    /* Use heap-allocated buffer array. */
+    bufs = (uv_buf_t*)uv__calloc(buf_count, sizeof(uv_buf_t));
+    if (bufs == NULL)
+      return ERROR_NOT_ENOUGH_MEMORY; /* Maps to UV_ENOMEM. */
+  }
+  buf_index = 0;
+
+  /* Initialize frame header and add it to the buffers list. */
+  memset(&frame_header, 0, sizeof frame_header);
+  bufs[buf_index++] = uv_buf_init((char*) &frame_header, sizeof frame_header);
+
+  if (send_handle != NULL) {
+    /* Add frame header flags. */
+    switch (xfer_type) {
+      case UV__IPC_SOCKET_XFER_TCP_CONNECTION:
+        frame_header.flags |= UV__IPC_FRAME_HAS_SOCKET_XFER |
+                              UV__IPC_FRAME_XFER_IS_TCP_CONNECTION;
+        break;
+      case UV__IPC_SOCKET_XFER_TCP_SERVER:
+        frame_header.flags |= UV__IPC_FRAME_HAS_SOCKET_XFER;
+        break;
+      default:
+        assert(0);  /* Unreachable. */
+    }
+    /* Add xfer info buffer. */
+    bufs[buf_index++] = uv_buf_init((char*) &xfer_info, sizeof xfer_info);
+  }
+
+  if (data_length > 0) {
+    /* Update frame header. */
+    frame_header.flags |= UV__IPC_FRAME_HAS_DATA;
+    frame_header.data_length = (uint32_t) data_length;
+    /* Add data buffers to buffers list. */
+    for (i = 0; i < data_buf_count; i++)
+      bufs[buf_index++] = data_bufs[i];
+  }
+
+  /* Write buffers. We set the `always_copy` flag, so it is not a problem that
+   * some of the written data lives on the stack. */
+  err = uv__pipe_write_data(loop, req, handle, bufs, buf_count, cb, 1);
+
+  /* If we had to heap-allocate the bufs array, free it now. */
+  if (bufs != stack_bufs) {
+    uv__free(bufs);
+  }
+
+  return err;
+}
+
+
+int uv__pipe_write(uv_loop_t* loop,
+                   uv_write_t* req,
+                   uv_pipe_t* handle,
+                   const uv_buf_t bufs[],
+                   size_t nbufs,
+                   uv_stream_t* send_handle,
+                   uv_write_cb cb) {
+  if (handle->ipc) {
+    /* IPC pipe write: use framing protocol. */
+    return uv__pipe_write_ipc(loop, req, handle, bufs, nbufs, send_handle, cb);
+  } else {
+    /* Non-IPC pipe write: put data on the wire directly. */
+    assert(send_handle == NULL);
+    return uv__pipe_write_data(loop, req, handle, bufs, nbufs, cb, 0);
+  }
+}
+
+
+static void uv__pipe_read_eof(uv_loop_t* loop, uv_pipe_t* handle,
+    uv_buf_t buf) {
+  /* If there is an eof timer running, we don't need it any more, so discard
+   * it. */
+  eof_timer_destroy(handle);
+
+  uv_read_stop((uv_stream_t*) handle);
+
+  handle->read_cb((uv_stream_t*) handle, UV_EOF, &buf);
+}
+
+
+static void uv__pipe_read_error(uv_loop_t* loop, uv_pipe_t* handle, int error,
+    uv_buf_t buf) {
+  /* If there is an eof timer running, we don't need it any more, so discard
+   * it. */
+  eof_timer_destroy(handle);
+
+  uv_read_stop((uv_stream_t*) handle);
+
+  handle->read_cb((uv_stream_t*)handle, uv_translate_sys_error(error), &buf);
+}
+
+
+static void uv__pipe_read_error_or_eof(uv_loop_t* loop, uv_pipe_t* handle,
+    int error, uv_buf_t buf) {
+  if (error == ERROR_BROKEN_PIPE) {
+    uv__pipe_read_eof(loop, handle, buf);
+  } else {
+    uv__pipe_read_error(loop, handle, error, buf);
+  }
+}
+
+
+static void uv__pipe_queue_ipc_xfer_info(
+    uv_pipe_t* handle,
+    uv__ipc_socket_xfer_type_t xfer_type,
+    uv__ipc_socket_xfer_info_t* xfer_info) {
+  uv__ipc_xfer_queue_item_t* item;
+
+  item = (uv__ipc_xfer_queue_item_t*) uv__malloc(sizeof(*item));
+  if (item == NULL)
+    uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc");
+
+  item->xfer_type = xfer_type;
+  item->xfer_info = *xfer_info;
+
+  QUEUE_INSERT_TAIL(&handle->pipe.conn.ipc_xfer_queue, &item->member);
+  handle->pipe.conn.ipc_xfer_queue_length++;
+}
+
+
+/* Read an exact number of bytes from a pipe. If an error or end-of-file is
+ * encountered before the requested number of bytes are read, an error is
+ * returned. */
+static int uv__pipe_read_exactly(HANDLE h, void* buffer, DWORD count) {
+  DWORD bytes_read, bytes_read_now;
+
+  bytes_read = 0;
+  while (bytes_read < count) {
+    if (!ReadFile(h,
+                  (char*) buffer + bytes_read,
+                  count - bytes_read,
+                  &bytes_read_now,
+                  NULL)) {
+      return GetLastError();
+    }
+
+    bytes_read += bytes_read_now;
+  }
+
+  assert(bytes_read == count);
+  return 0;
+}
+
+
+static DWORD uv__pipe_read_data(uv_loop_t* loop,
+                                uv_pipe_t* handle,
+                                DWORD suggested_bytes,
+                                DWORD max_bytes) {
+  DWORD bytes_read;
+  uv_buf_t buf;
+
+  /* Ask the user for a buffer to read data into. */
+  buf = uv_buf_init(NULL, 0);
+  handle->alloc_cb((uv_handle_t*) handle, suggested_bytes, &buf);
+  if (buf.base == NULL || buf.len == 0) {
+    handle->read_cb((uv_stream_t*) handle, UV_ENOBUFS, &buf);
+    return 0; /* Break out of read loop. */
+  }
+
+  /* Ensure we read at most the smaller of:
+   *   (a) the length of the user-allocated buffer.
+   *   (b) the maximum data length as specified by the `max_bytes` argument.
+   */
+  if (max_bytes > buf.len)
+    max_bytes = buf.len;
+
+  /* Read into the user buffer. */
+  if (!ReadFile(handle->handle, buf.base, max_bytes, &bytes_read, NULL)) {
+    uv__pipe_read_error_or_eof(loop, handle, GetLastError(), buf);
+    return 0; /* Break out of read loop. */
+  }
+
+  /* Call the read callback. */
+  handle->read_cb((uv_stream_t*) handle, bytes_read, &buf);
+
+  return bytes_read;
+}
+
+
+static DWORD uv__pipe_read_ipc(uv_loop_t* loop, uv_pipe_t* handle) {
+  uint32_t* data_remaining = &handle->pipe.conn.ipc_data_frame.payload_remaining;
+  int err;
+
+  if (*data_remaining > 0) {
+    /* Read frame data payload. */
+    DWORD bytes_read =
+        uv__pipe_read_data(loop, handle, *data_remaining, *data_remaining);
+    *data_remaining -= bytes_read;
+    return bytes_read;
+
+  } else {
+    /* Start of a new IPC frame. */
+    uv__ipc_frame_header_t frame_header;
+    uint32_t xfer_flags;
+    uv__ipc_socket_xfer_type_t xfer_type;
+    uv__ipc_socket_xfer_info_t xfer_info;
+
+    /* Read the IPC frame header. */
+    err = uv__pipe_read_exactly(
+        handle->handle, &frame_header, sizeof frame_header);
+    if (err)
+      goto error;
+
+    /* Validate that flags are valid. */
+    if ((frame_header.flags & ~UV__IPC_FRAME_VALID_FLAGS) != 0)
+      goto invalid;
+    /* Validate that reserved2 is zero. */
+    if (frame_header.reserved2 != 0)
+      goto invalid;
+
+    /* Parse xfer flags. */
+    xfer_flags = frame_header.flags & UV__IPC_FRAME_XFER_FLAGS;
+    if (xfer_flags & UV__IPC_FRAME_HAS_SOCKET_XFER) {
+      /* Socket coming -- determine the type. */
+      xfer_type = xfer_flags & UV__IPC_FRAME_XFER_IS_TCP_CONNECTION
+                      ? UV__IPC_SOCKET_XFER_TCP_CONNECTION
+                      : UV__IPC_SOCKET_XFER_TCP_SERVER;
+    } else if (xfer_flags == 0) {
+      /* No socket. */
+      xfer_type = UV__IPC_SOCKET_XFER_NONE;
+    } else {
+      /* Invalid flags. */
+      goto invalid;
+    }
+
+    /* Parse data frame information. */
+    if (frame_header.flags & UV__IPC_FRAME_HAS_DATA) {
+      *data_remaining = frame_header.data_length;
+    } else if (frame_header.data_length != 0) {
+      /* Data length greater than zero but data flag not set -- invalid. */
+      goto invalid;
+    }
+
+    /* If no socket xfer info follows, return here. Data will be read in a
+     * subsequent invocation of uv__pipe_read_ipc(). */
+    if (xfer_type == UV__IPC_SOCKET_XFER_NONE)
+      return sizeof frame_header; /* Number of bytes read. */
+
+    /* Read transferred socket information. */
+    err = uv__pipe_read_exactly(handle->handle, &xfer_info, sizeof xfer_info);
+    if (err)
+      goto error;
+
+    /* Store the pending socket info. */
+    uv__pipe_queue_ipc_xfer_info(handle, xfer_type, &xfer_info);
+
+    /* Return number of bytes read. */
+    return sizeof frame_header + sizeof xfer_info;
+  }
+
+invalid:
+  /* Invalid frame. */
+  err = WSAECONNABORTED; /* Maps to UV_ECONNABORTED. */
+
+error:
+  uv__pipe_read_error_or_eof(loop, handle, err, uv_null_buf_);
+  return 0; /* Break out of read loop. */
+}
+
+
+void uv__process_pipe_read_req(uv_loop_t* loop,
+                               uv_pipe_t* handle,
+                               uv_req_t* req) {
+  assert(handle->type == UV_NAMED_PIPE);
+
+  handle->flags &= ~(UV_HANDLE_READ_PENDING | UV_HANDLE_CANCELLATION_PENDING);
+  DECREASE_PENDING_REQ_COUNT(handle);
+  eof_timer_stop(handle);
+
+  /* At this point, we're done with bookkeeping. If the user has stopped
+   * reading the pipe in the meantime, there is nothing left to do, since there
+   * is no callback that we can call. */
+  if (!(handle->flags & UV_HANDLE_READING))
+    return;
+
+  if (!REQ_SUCCESS(req)) {
+    /* An error occurred doing the zero-read. */
+    DWORD err = GET_REQ_ERROR(req);
+
+    /* If the read was cancelled by uv__pipe_interrupt_read(), the request may
+     * indicate an ERROR_OPERATION_ABORTED error. This error isn't relevant to
+     * the user; we'll start a new zero-read at the end of this function. */
+    if (err != ERROR_OPERATION_ABORTED)
+      uv__pipe_read_error_or_eof(loop, handle, err, uv_null_buf_);
+
+  } else {
+    /* The zero-read completed without error, indicating there is data
+     * available in the kernel buffer. */
+    DWORD avail;
+
+    /* Get the number of bytes available. */
+    avail = 0;
+    if (!PeekNamedPipe(handle->handle, NULL, 0, NULL, &avail, NULL))
+      uv__pipe_read_error_or_eof(loop, handle, GetLastError(), uv_null_buf_);
+
+    /* Read until we've either read all the bytes available, or the 'reading'
+     * flag is cleared. */
+    while (avail > 0 && handle->flags & UV_HANDLE_READING) {
+      /* Depending on the type of pipe, read either IPC frames or raw data. */
+      DWORD bytes_read =
+          handle->ipc ? uv__pipe_read_ipc(loop, handle)
+                      : uv__pipe_read_data(loop, handle, avail, (DWORD) -1);
+
+      /* If no bytes were read, treat this as an indication that an error
+       * occurred, and break out of the read loop. */
+      if (bytes_read == 0)
+        break;
+
+      /* It is possible that more bytes were read than we thought were
+       * available. To prevent `avail` from underflowing, break out of the loop
+       * if this is the case. */
+      if (bytes_read > avail)
+        break;
+
+      /* Recompute the number of bytes available. */
+      avail -= bytes_read;
+    }
+  }
+
+  /* Start another zero-read request if necessary. */
+  if ((handle->flags & UV_HANDLE_READING) &&
+      !(handle->flags & UV_HANDLE_READ_PENDING)) {
+    uv__pipe_queue_read(loop, handle);
+  }
+}
+
+
+void uv__process_pipe_write_req(uv_loop_t* loop, uv_pipe_t* handle,
+    uv_write_t* req) {
+  int err;
+
+  assert(handle->type == UV_NAMED_PIPE);
+
+  assert(handle->write_queue_size >= req->u.io.queued_bytes);
+  handle->write_queue_size -= req->u.io.queued_bytes;
+
+  UNREGISTER_HANDLE_REQ(loop, handle, req);
+
+  if (handle->flags & UV_HANDLE_EMULATE_IOCP) {
+    if (req->wait_handle != INVALID_HANDLE_VALUE) {
+      UnregisterWait(req->wait_handle);
+      req->wait_handle = INVALID_HANDLE_VALUE;
+    }
+    if (req->event_handle) {
+      CloseHandle(req->event_handle);
+      req->event_handle = NULL;
+    }
+  }
+
+  err = GET_REQ_ERROR(req);
+
+  /* If this was a coalesced write, extract pointer to the user_provided
+   * uv_write_t structure so we can pass the expected pointer to the callback,
+   * then free the heap-allocated write req. */
+  if (req->coalesced) {
+    uv__coalesced_write_t* coalesced_write =
+        container_of(req, uv__coalesced_write_t, req);
+    req = coalesced_write->user_req;
+    uv__free(coalesced_write);
+  }
+  if (req->cb) {
+    req->cb(req, uv_translate_sys_error(err));
+  }
+
+  handle->stream.conn.write_reqs_pending--;
+
+  if (handle->flags & UV_HANDLE_NON_OVERLAPPED_PIPE &&
+      handle->pipe.conn.non_overlapped_writes_tail) {
+    assert(handle->stream.conn.write_reqs_pending > 0);
+    uv__queue_non_overlapped_write(handle);
+  }
+
+  if (handle->stream.conn.write_reqs_pending == 0)
+    if (handle->flags & UV_HANDLE_SHUTTING)
+      uv__pipe_shutdown(loop, handle, handle->stream.conn.shutdown_req);
+
+  DECREASE_PENDING_REQ_COUNT(handle);
+}
+
+
+void uv__process_pipe_accept_req(uv_loop_t* loop, uv_pipe_t* handle,
+    uv_req_t* raw_req) {
+  uv_pipe_accept_t* req = (uv_pipe_accept_t*) raw_req;
+
+  assert(handle->type == UV_NAMED_PIPE);
+
+  if (handle->flags & UV_HANDLE_CLOSING) {
+    /* The req->pipeHandle should be freed already in uv__pipe_close(). */
+    assert(req->pipeHandle == INVALID_HANDLE_VALUE);
+    DECREASE_PENDING_REQ_COUNT(handle);
+    return;
+  }
+
+  if (REQ_SUCCESS(req)) {
+    assert(req->pipeHandle != INVALID_HANDLE_VALUE);
+    req->next_pending = handle->pipe.serv.pending_accepts;
+    handle->pipe.serv.pending_accepts = req;
+
+    if (handle->stream.serv.connection_cb) {
+      handle->stream.serv.connection_cb((uv_stream_t*)handle, 0);
+    }
+  } else {
+    if (req->pipeHandle != INVALID_HANDLE_VALUE) {
+      CloseHandle(req->pipeHandle);
+      req->pipeHandle = INVALID_HANDLE_VALUE;
+    }
+    if (!(handle->flags & UV_HANDLE_CLOSING)) {
+      uv__pipe_queue_accept(loop, handle, req, FALSE);
+    }
+  }
+
+  DECREASE_PENDING_REQ_COUNT(handle);
+}
+
+
+void uv__process_pipe_connect_req(uv_loop_t* loop, uv_pipe_t* handle,
+    uv_connect_t* req) {
+  HANDLE pipeHandle;
+  DWORD duplex_flags;
+  int err;
+
+  assert(handle->type == UV_NAMED_PIPE);
+
+  UNREGISTER_HANDLE_REQ(loop, handle, req);
+
+  err = 0;
+  if (REQ_SUCCESS(req)) {
+    pipeHandle = req->u.connect.pipeHandle;
+    duplex_flags = req->u.connect.duplex_flags;
+    err = uv__set_pipe_handle(loop, handle, pipeHandle, -1, duplex_flags);
+    if (err)
+      CloseHandle(pipeHandle);
+  } else {
+    err = uv_translate_sys_error(GET_REQ_ERROR(req));
+  }
+
+  if (req->cb)
+    req->cb(req, err);
+
+  DECREASE_PENDING_REQ_COUNT(handle);
+}
+
+
+
+void uv__process_pipe_shutdown_req(uv_loop_t* loop, uv_pipe_t* handle,
+    uv_shutdown_t* req) {
+  int err;
+
+  assert(handle->type == UV_NAMED_PIPE);
+
+  /* Clear the shutdown_req field so we don't go here again. */
+  handle->stream.conn.shutdown_req = NULL;
+  handle->flags &= ~UV_HANDLE_SHUTTING;
+  UNREGISTER_HANDLE_REQ(loop, handle, req);
+
+  if (handle->flags & UV_HANDLE_CLOSING) {
+    /* Already closing. Cancel the shutdown. */
+    err = UV_ECANCELED;
+  } else if (!REQ_SUCCESS(req)) {
+    /* An error occurred in trying to shutdown gracefully. */
+    err = uv_translate_sys_error(GET_REQ_ERROR(req));
+  } else {
+    if (handle->flags & UV_HANDLE_READABLE) {
+      /* Initialize and optionally start the eof timer. Only do this if the pipe
+       * is readable and we haven't seen EOF come in ourselves. */
+      eof_timer_init(handle);
+
+      /* If reading start the timer right now. Otherwise uv__pipe_queue_read will
+       * start it. */
+      if (handle->flags & UV_HANDLE_READ_PENDING) {
+        eof_timer_start(handle);
+      }
+
+    } else {
+      /* This pipe is not readable. We can just close it to let the other end
+       * know that we're done writing. */
+      close_pipe(handle);
+    }
+    err = 0;
+  }
+
+  if (req->cb)
+    req->cb(req, err);
+
+  DECREASE_PENDING_REQ_COUNT(handle);
+}
+
+
+static void eof_timer_init(uv_pipe_t* pipe) {
+  int r;
+
+  assert(pipe->pipe.conn.eof_timer == NULL);
+  assert(pipe->flags & UV_HANDLE_CONNECTION);
+
+  pipe->pipe.conn.eof_timer = (uv_timer_t*) uv__malloc(sizeof *pipe->pipe.conn.eof_timer);
+
+  r = uv_timer_init(pipe->loop, pipe->pipe.conn.eof_timer);
+  assert(r == 0);  /* timers can't fail */
+  (void) r;
+  pipe->pipe.conn.eof_timer->data = pipe;
+  uv_unref((uv_handle_t*) pipe->pipe.conn.eof_timer);
+}
+
+
+static void eof_timer_start(uv_pipe_t* pipe) {
+  assert(pipe->flags & UV_HANDLE_CONNECTION);
+
+  if (pipe->pipe.conn.eof_timer != NULL) {
+    uv_timer_start(pipe->pipe.conn.eof_timer, eof_timer_cb, eof_timeout, 0);
+  }
+}
+
+
+static void eof_timer_stop(uv_pipe_t* pipe) {
+  assert(pipe->flags & UV_HANDLE_CONNECTION);
+
+  if (pipe->pipe.conn.eof_timer != NULL) {
+    uv_timer_stop(pipe->pipe.conn.eof_timer);
+  }
+}
+
+
+static void eof_timer_cb(uv_timer_t* timer) {
+  uv_pipe_t* pipe = (uv_pipe_t*) timer->data;
+  uv_loop_t* loop = timer->loop;
+
+  assert(pipe->type == UV_NAMED_PIPE);
+
+  /* This should always be true, since we start the timer only in
+   * uv__pipe_queue_read after successfully calling ReadFile, or in
+   * uv__process_pipe_shutdown_req if a read is pending, and we always
+   * immediately stop the timer in uv__process_pipe_read_req. */
+  assert(pipe->flags & UV_HANDLE_READ_PENDING);
+
+  /* If there are many packets coming off the iocp then the timer callback may
+   * be called before the read request is coming off the queue. Therefore we
+   * check here if the read request has completed but will be processed later.
+   */
+  if ((pipe->flags & UV_HANDLE_READ_PENDING) &&
+      HasOverlappedIoCompleted(&pipe->read_req.u.io.overlapped)) {
+    return;
+  }
+
+  /* Force both ends off the pipe. */
+  close_pipe(pipe);
+
+  /* Stop reading, so the pending read that is going to fail will not be
+   * reported to the user. */
+  uv_read_stop((uv_stream_t*) pipe);
+
+  /* Report the eof and update flags. This will get reported even if the user
+   * stopped reading in the meantime. TODO: is that okay? */
+  uv__pipe_read_eof(loop, pipe, uv_null_buf_);
+}
+
+
+static void eof_timer_destroy(uv_pipe_t* pipe) {
+  assert(pipe->flags & UV_HANDLE_CONNECTION);
+
+  if (pipe->pipe.conn.eof_timer) {
+    uv_close((uv_handle_t*) pipe->pipe.conn.eof_timer, eof_timer_close_cb);
+    pipe->pipe.conn.eof_timer = NULL;
+  }
+}
+
+
+static void eof_timer_close_cb(uv_handle_t* handle) {
+  assert(handle->type == UV_TIMER);
+  uv__free(handle);
+}
+
+
+int uv_pipe_open(uv_pipe_t* pipe, uv_file file) {
+  HANDLE os_handle = uv__get_osfhandle(file);
+  NTSTATUS nt_status;
+  IO_STATUS_BLOCK io_status;
+  FILE_ACCESS_INFORMATION access;
+  DWORD duplex_flags = 0;
+  int err;
+
+  if (os_handle == INVALID_HANDLE_VALUE)
+    return UV_EBADF;
+  if (pipe->flags & UV_HANDLE_PIPESERVER)
+    return UV_EINVAL;
+  if (pipe->flags & UV_HANDLE_CONNECTION)
+    return UV_EBUSY;
+
+  uv__pipe_connection_init(pipe);
+  uv__once_init();
+  /* In order to avoid closing a stdio file descriptor 0-2, duplicate the
+   * underlying OS handle and forget about the original fd.
+   * We could also opt to use the original OS handle and just never close it,
+   * but then there would be no reliable way to cancel pending read operations
+   * upon close.
+   */
+  if (file <= 2) {
+    if (!DuplicateHandle(INVALID_HANDLE_VALUE,
+                         os_handle,
+                         INVALID_HANDLE_VALUE,
+                         &os_handle,
+                         0,
+                         FALSE,
+                         DUPLICATE_SAME_ACCESS))
+      return uv_translate_sys_error(GetLastError());
+    assert(os_handle != INVALID_HANDLE_VALUE);
+    file = -1;
+  }
+
+  /* Determine what kind of permissions we have on this handle.
+   * Cygwin opens the pipe in message mode, but we can support it,
+   * just query the access flags and set the stream flags accordingly.
+   */
+  nt_status = pNtQueryInformationFile(os_handle,
+                                      &io_status,
+                                      &access,
+                                      sizeof(access),
+                                      FileAccessInformation);
+  if (nt_status != STATUS_SUCCESS)
+    return UV_EINVAL;
+
+  if (pipe->ipc) {
+    if (!(access.AccessFlags & FILE_WRITE_DATA) ||
+        !(access.AccessFlags & FILE_READ_DATA)) {
+      return UV_EINVAL;
+    }
+  }
+
+  if (access.AccessFlags & FILE_WRITE_DATA)
+    duplex_flags |= UV_HANDLE_WRITABLE;
+  if (access.AccessFlags & FILE_READ_DATA)
+    duplex_flags |= UV_HANDLE_READABLE;
+
+  err = uv__set_pipe_handle(pipe->loop,
+                            pipe,
+                            os_handle,
+                            file,
+                            duplex_flags);
+  if (err) {
+    if (file == -1)
+      CloseHandle(os_handle);
+    return err;
+  }
+
+  if (pipe->ipc) {
+    assert(!(pipe->flags & UV_HANDLE_NON_OVERLAPPED_PIPE));
+    pipe->pipe.conn.ipc_remote_pid = uv_os_getppid();
+    assert(pipe->pipe.conn.ipc_remote_pid != (DWORD)(uv_pid_t) -1);
+  }
+  return 0;
+}
+
+
+static int uv__pipe_getname(const uv_pipe_t* handle, char* buffer, size_t* size) {
+  NTSTATUS nt_status;
+  IO_STATUS_BLOCK io_status;
+  FILE_NAME_INFORMATION tmp_name_info;
+  FILE_NAME_INFORMATION* name_info;
+  WCHAR* name_buf;
+  unsigned int addrlen;
+  unsigned int name_size;
+  unsigned int name_len;
+  int err;
+
+  uv__once_init();
+  name_info = NULL;
+
+  if (handle->name != NULL) {
+    /* The user might try to query the name before we are connected,
+     * and this is just easier to return the cached value if we have it. */
+    name_buf = handle->name;
+    name_len = wcslen(name_buf);
+
+    /* check how much space we need */
+    addrlen = WideCharToMultiByte(CP_UTF8,
+                                  0,
+                                  name_buf,
+                                  name_len,
+                                  NULL,
+                                  0,
+                                  NULL,
+                                  NULL);
+    if (!addrlen) {
+      *size = 0;
+      err = uv_translate_sys_error(GetLastError());
+      return err;
+    } else if (addrlen >= *size) {
+      *size = addrlen + 1;
+      err = UV_ENOBUFS;
+      goto error;
+    }
+
+    addrlen = WideCharToMultiByte(CP_UTF8,
+                                  0,
+                                  name_buf,
+                                  name_len,
+                                  buffer,
+                                  addrlen,
+                                  NULL,
+                                  NULL);
+    if (!addrlen) {
+      *size = 0;
+      err = uv_translate_sys_error(GetLastError());
+      return err;
+    }
+
+    *size = addrlen;
+    buffer[addrlen] = '\0';
+
+    return 0;
+  }
+
+  if (handle->handle == INVALID_HANDLE_VALUE) {
+    *size = 0;
+    return UV_EINVAL;
+  }
+
+  /* NtQueryInformationFile will block if another thread is performing a
+   * blocking operation on the queried handle. If the pipe handle is
+   * synchronous, there may be a worker thread currently calling ReadFile() on
+   * the pipe handle, which could cause a deadlock. To avoid this, interrupt
+   * the read. */
+  if (handle->flags & UV_HANDLE_CONNECTION &&
+      handle->flags & UV_HANDLE_NON_OVERLAPPED_PIPE) {
+    uv__pipe_interrupt_read((uv_pipe_t*) handle); /* cast away const warning */
+  }
+
+  nt_status = pNtQueryInformationFile(handle->handle,
+                                      &io_status,
+                                      &tmp_name_info,
+                                      sizeof tmp_name_info,
+                                      FileNameInformation);
+  if (nt_status == STATUS_BUFFER_OVERFLOW) {
+    name_size = sizeof(*name_info) + tmp_name_info.FileNameLength;
+    name_info = (FILE_NAME_INFORMATION*)uv__malloc(name_size);
+    if (!name_info) {
+      *size = 0;
+      err = UV_ENOMEM;
+      goto cleanup;
+    }
+
+    nt_status = pNtQueryInformationFile(handle->handle,
+                                        &io_status,
+                                        name_info,
+                                        name_size,
+                                        FileNameInformation);
+  }
+
+  if (nt_status != STATUS_SUCCESS) {
+    *size = 0;
+    err = uv_translate_sys_error(pRtlNtStatusToDosError(nt_status));
+    goto error;
+  }
+
+  if (!name_info) {
+    /* the struct on stack was used */
+    name_buf = tmp_name_info.FileName;
+    name_len = tmp_name_info.FileNameLength;
+  } else {
+    name_buf = name_info->FileName;
+    name_len = name_info->FileNameLength;
+  }
+
+  if (name_len == 0) {
+    *size = 0;
+    err = 0;
+    goto error;
+  }
+
+  name_len /= sizeof(WCHAR);
+
+  /* check how much space we need */
+  addrlen = WideCharToMultiByte(CP_UTF8,
+                                0,
+                                name_buf,
+                                name_len,
+                                NULL,
+                                0,
+                                NULL,
+                                NULL);
+  if (!addrlen) {
+    *size = 0;
+    err = uv_translate_sys_error(GetLastError());
+    goto error;
+  } else if (pipe_prefix_len + addrlen >= *size) {
+    /* "\\\\.\\pipe" + name */
+    *size = pipe_prefix_len + addrlen + 1;
+    err = UV_ENOBUFS;
+    goto error;
+  }
+
+  memcpy(buffer, pipe_prefix, pipe_prefix_len);
+  addrlen = WideCharToMultiByte(CP_UTF8,
+                                0,
+                                name_buf,
+                                name_len,
+                                buffer+pipe_prefix_len,
+                                *size-pipe_prefix_len,
+                                NULL,
+                                NULL);
+  if (!addrlen) {
+    *size = 0;
+    err = uv_translate_sys_error(GetLastError());
+    goto error;
+  }
+
+  addrlen += pipe_prefix_len;
+  *size = addrlen;
+  buffer[addrlen] = '\0';
+
+  err = 0;
+
+error:
+  uv__free(name_info);
+
+cleanup:
+  return err;
+}
+
+
+int uv_pipe_pending_count(uv_pipe_t* handle) {
+  if (!handle->ipc)
+    return 0;
+  return handle->pipe.conn.ipc_xfer_queue_length;
+}
+
+
+int uv_pipe_getsockname(const uv_pipe_t* handle, char* buffer, size_t* size) {
+  if (handle->flags & UV_HANDLE_BOUND)
+    return uv__pipe_getname(handle, buffer, size);
+
+  if (handle->flags & UV_HANDLE_CONNECTION ||
+      handle->handle != INVALID_HANDLE_VALUE) {
+    *size = 0;
+    return 0;
+  }
+
+  return UV_EBADF;
+}
+
+
+int uv_pipe_getpeername(const uv_pipe_t* handle, char* buffer, size_t* size) {
+  /* emulate unix behaviour */
+  if (handle->flags & UV_HANDLE_BOUND)
+    return UV_ENOTCONN;
+
+  if (handle->handle != INVALID_HANDLE_VALUE)
+    return uv__pipe_getname(handle, buffer, size);
+
+  if (handle->flags & UV_HANDLE_CONNECTION) {
+    if (handle->name != NULL)
+      return uv__pipe_getname(handle, buffer, size);
+  }
+
+  return UV_EBADF;
+}
+
+
+uv_handle_type uv_pipe_pending_type(uv_pipe_t* handle) {
+  if (!handle->ipc)
+    return UV_UNKNOWN_HANDLE;
+  if (handle->pipe.conn.ipc_xfer_queue_length == 0)
+    return UV_UNKNOWN_HANDLE;
+  else
+    return UV_TCP;
+}
+
+int uv_pipe_chmod(uv_pipe_t* handle, int mode) {
+  SID_IDENTIFIER_AUTHORITY sid_world = { SECURITY_WORLD_SID_AUTHORITY };
+  PACL old_dacl, new_dacl;
+  PSECURITY_DESCRIPTOR sd;
+  EXPLICIT_ACCESS ea;
+  PSID everyone;
+  int error;
+
+  if (handle == NULL || handle->handle == INVALID_HANDLE_VALUE)
+    return UV_EBADF;
+
+  if (mode != UV_READABLE &&
+      mode != UV_WRITABLE &&
+      mode != (UV_WRITABLE | UV_READABLE))
+    return UV_EINVAL;
+
+  if (!AllocateAndInitializeSid(&sid_world,
+                                1,
+                                SECURITY_WORLD_RID,
+                                0, 0, 0, 0, 0, 0, 0,
+                                &everyone)) {
+    error = GetLastError();
+    goto done;
+  }
+
+  if (GetSecurityInfo(handle->handle,
+                      SE_KERNEL_OBJECT,
+                      DACL_SECURITY_INFORMATION,
+                      NULL,
+                      NULL,
+                      &old_dacl,
+                      NULL,
+                      &sd)) {
+    error = GetLastError();
+    goto clean_sid;
+  }
+
+  memset(&ea, 0, sizeof(EXPLICIT_ACCESS));
+  if (mode & UV_READABLE)
+    ea.grfAccessPermissions |= GENERIC_READ | FILE_WRITE_ATTRIBUTES;
+  if (mode & UV_WRITABLE)
+    ea.grfAccessPermissions |= GENERIC_WRITE | FILE_READ_ATTRIBUTES;
+  ea.grfAccessPermissions |= SYNCHRONIZE;
+  ea.grfAccessMode = SET_ACCESS;
+  ea.grfInheritance = NO_INHERITANCE;
+  ea.Trustee.TrusteeForm = TRUSTEE_IS_SID;
+  ea.Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP;
+  ea.Trustee.ptstrName = (LPTSTR)everyone;
+
+  if (SetEntriesInAcl(1, &ea, old_dacl, &new_dacl)) {
+    error = GetLastError();
+    goto clean_sd;
+  }
+
+  if (SetSecurityInfo(handle->handle,
+                      SE_KERNEL_OBJECT,
+                      DACL_SECURITY_INFORMATION,
+                      NULL,
+                      NULL,
+                      new_dacl,
+                      NULL)) {
+    error = GetLastError();
+    goto clean_dacl;
+  }
+
+  error = 0;
+
+clean_dacl:
+  LocalFree((HLOCAL) new_dacl);
+clean_sd:
+  LocalFree((HLOCAL) sd);
+clean_sid:
+  FreeSid(everyone);
+done:
+  return uv_translate_sys_error(error);
+}
diff --git a/wpinet/src/main/native/thirdparty/libuv/src/win/poll.cpp b/wpinet/src/main/native/thirdparty/libuv/src/win/poll.cpp
new file mode 100644
index 0000000..bd531b0
--- /dev/null
+++ b/wpinet/src/main/native/thirdparty/libuv/src/win/poll.cpp
@@ -0,0 +1,587 @@
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include <assert.h>
+#include <io.h>
+
+#include "uv.h"
+#include "internal.h"
+#include "handle-inl.h"
+#include "req-inl.h"
+
+
+static const GUID uv_msafd_provider_ids[UV_MSAFD_PROVIDER_COUNT] = {
+  {0xe70f1aa0, 0xab8b, 0x11cf,
+      {0x8c, 0xa3, 0x00, 0x80, 0x5f, 0x48, 0xa1, 0x92}},
+  {0xf9eab0c0, 0x26d4, 0x11d0,
+      {0xbb, 0xbf, 0x00, 0xaa, 0x00, 0x6c, 0x34, 0xe4}},
+  {0x9fc48064, 0x7298, 0x43e4,
+      {0xb7, 0xbd, 0x18, 0x1f, 0x20, 0x89, 0x79, 0x2a}},
+  {0xa00943d9, 0x9c2e, 0x4633,
+      {0x9b, 0x59, 0x00, 0x57, 0xa3, 0x16, 0x09, 0x94}}
+};
+
+typedef struct uv_single_fd_set_s {
+  unsigned int fd_count;
+  SOCKET fd_array[1];
+} uv_single_fd_set_t;
+
+
+static OVERLAPPED overlapped_dummy_;
+static uv_once_t overlapped_dummy_init_guard_ = UV_ONCE_INIT;
+
+static AFD_POLL_INFO afd_poll_info_dummy_;
+
+
+static void uv__init_overlapped_dummy(void) {
+  HANDLE event;
+
+  event = CreateEvent(NULL, TRUE, TRUE, NULL);
+  if (event == NULL)
+    uv_fatal_error(GetLastError(), "CreateEvent");
+
+  memset(&overlapped_dummy_, 0, sizeof overlapped_dummy_);
+  overlapped_dummy_.hEvent = (HANDLE) ((uintptr_t) event | 1);
+}
+
+
+static OVERLAPPED* uv__get_overlapped_dummy(void) {
+  uv_once(&overlapped_dummy_init_guard_, uv__init_overlapped_dummy);
+  return &overlapped_dummy_;
+}
+
+
+static AFD_POLL_INFO* uv__get_afd_poll_info_dummy(void) {
+  return &afd_poll_info_dummy_;
+}
+
+
+static void uv__fast_poll_submit_poll_req(uv_loop_t* loop, uv_poll_t* handle) {
+  uv_req_t* req;
+  AFD_POLL_INFO* afd_poll_info;
+  int result;
+
+  /* Find a yet unsubmitted req to submit. */
+  if (handle->submitted_events_1 == 0) {
+    req = &handle->poll_req_1;
+    afd_poll_info = &handle->afd_poll_info_1;
+    handle->submitted_events_1 = handle->events;
+    handle->mask_events_1 = 0;
+    handle->mask_events_2 = handle->events;
+  } else if (handle->submitted_events_2 == 0) {
+    req = &handle->poll_req_2;
+    afd_poll_info = &handle->afd_poll_info_2;
+    handle->submitted_events_2 = handle->events;
+    handle->mask_events_1 = handle->events;
+    handle->mask_events_2 = 0;
+  } else {
+    /* Just wait until there's an unsubmitted req. This will happen almost
+     * immediately as one of the 2 outstanding requests is about to return.
+     * When this happens, uv__fast_poll_process_poll_req will be called, and
+     * the pending events, if needed, will be processed in a subsequent
+     * request. */
+    return;
+  }
+
+  /* Setting Exclusive to TRUE makes the other poll request return if there is
+   * any. */
+  afd_poll_info->Exclusive = TRUE;
+  afd_poll_info->NumberOfHandles = 1;
+  afd_poll_info->Timeout.QuadPart = INT64_MAX;
+  afd_poll_info->Handles[0].Handle = (HANDLE) handle->socket;
+  afd_poll_info->Handles[0].Status = 0;
+  afd_poll_info->Handles[0].Events = 0;
+
+  if (handle->events & UV_READABLE) {
+    afd_poll_info->Handles[0].Events |= AFD_POLL_RECEIVE |
+        AFD_POLL_DISCONNECT | AFD_POLL_ACCEPT | AFD_POLL_ABORT;
+  } else {
+    if (handle->events & UV_DISCONNECT) {
+      afd_poll_info->Handles[0].Events |= AFD_POLL_DISCONNECT;
+    }
+  }
+  if (handle->events & UV_WRITABLE) {
+    afd_poll_info->Handles[0].Events |= AFD_POLL_SEND | AFD_POLL_CONNECT_FAIL;
+  }
+
+  memset(&req->u.io.overlapped, 0, sizeof req->u.io.overlapped);
+
+  result = uv__msafd_poll((SOCKET) handle->peer_socket,
+                          afd_poll_info,
+                          afd_poll_info,
+                          &req->u.io.overlapped);
+  if (result != 0 && WSAGetLastError() != WSA_IO_PENDING) {
+    /* Queue this req, reporting an error. */
+    SET_REQ_ERROR(req, WSAGetLastError());
+    uv__insert_pending_req(loop, req);
+  }
+}
+
+
+static void uv__fast_poll_process_poll_req(uv_loop_t* loop, uv_poll_t* handle,
+    uv_req_t* req) {
+  unsigned char mask_events;
+  AFD_POLL_INFO* afd_poll_info;
+
+  if (req == &handle->poll_req_1) {
+    afd_poll_info = &handle->afd_poll_info_1;
+    handle->submitted_events_1 = 0;
+    mask_events = handle->mask_events_1;
+  } else if (req == &handle->poll_req_2) {
+    afd_poll_info = &handle->afd_poll_info_2;
+    handle->submitted_events_2 = 0;
+    mask_events = handle->mask_events_2;
+  } else {
+    assert(0);
+    return;
+  }
+
+  /* Report an error unless the select was just interrupted. */
+  if (!REQ_SUCCESS(req)) {
+    DWORD error = GET_REQ_SOCK_ERROR(req);
+    if (error != WSAEINTR && handle->events != 0) {
+      handle->events = 0; /* Stop the watcher */
+      handle->poll_cb(handle, uv_translate_sys_error(error), 0);
+    }
+
+  } else if (afd_poll_info->NumberOfHandles >= 1) {
+    unsigned char events = 0;
+
+    if ((afd_poll_info->Handles[0].Events & (AFD_POLL_RECEIVE |
+        AFD_POLL_DISCONNECT | AFD_POLL_ACCEPT | AFD_POLL_ABORT)) != 0) {
+      events |= UV_READABLE;
+      if ((afd_poll_info->Handles[0].Events & AFD_POLL_DISCONNECT) != 0) {
+        events |= UV_DISCONNECT;
+      }
+    }
+    if ((afd_poll_info->Handles[0].Events & (AFD_POLL_SEND |
+        AFD_POLL_CONNECT_FAIL)) != 0) {
+      events |= UV_WRITABLE;
+    }
+
+    events &= handle->events & ~mask_events;
+
+    if (afd_poll_info->Handles[0].Events & AFD_POLL_LOCAL_CLOSE) {
+      /* Stop polling. */
+      handle->events = 0;
+      if (uv__is_active(handle))
+        uv__handle_stop(handle);
+    }
+
+    if (events != 0) {
+      handle->poll_cb(handle, 0, events);
+    }
+  }
+
+  if ((handle->events & ~(handle->submitted_events_1 |
+      handle->submitted_events_2)) != 0) {
+    uv__fast_poll_submit_poll_req(loop, handle);
+  } else if ((handle->flags & UV_HANDLE_CLOSING) &&
+             handle->submitted_events_1 == 0 &&
+             handle->submitted_events_2 == 0) {
+    uv__want_endgame(loop, (uv_handle_t*) handle);
+  }
+}
+
+
+static SOCKET uv__fast_poll_create_peer_socket(HANDLE iocp,
+    WSAPROTOCOL_INFOW* protocol_info) {
+  SOCKET sock = 0;
+
+  sock = WSASocketW(protocol_info->iAddressFamily,
+                    protocol_info->iSocketType,
+                    protocol_info->iProtocol,
+                    protocol_info,
+                    0,
+                    WSA_FLAG_OVERLAPPED);
+  if (sock == INVALID_SOCKET) {
+    return INVALID_SOCKET;
+  }
+
+  if (!SetHandleInformation((HANDLE) sock, HANDLE_FLAG_INHERIT, 0)) {
+    goto error;
+  };
+
+  if (CreateIoCompletionPort((HANDLE) sock,
+                             iocp,
+                             (ULONG_PTR) sock,
+                             0) == NULL) {
+    goto error;
+  }
+
+  return sock;
+
+ error:
+  closesocket(sock);
+  return INVALID_SOCKET;
+}
+
+
+static SOCKET uv__fast_poll_get_peer_socket(uv_loop_t* loop,
+    WSAPROTOCOL_INFOW* protocol_info) {
+  int index, i;
+  SOCKET peer_socket;
+
+  index = -1;
+  for (i = 0; (size_t) i < ARRAY_SIZE(uv_msafd_provider_ids); i++) {
+    if (memcmp((void*) &protocol_info->ProviderId,
+               (void*) &uv_msafd_provider_ids[i],
+               sizeof protocol_info->ProviderId) == 0) {
+      index = i;
+    }
+  }
+
+  /* Check if the protocol uses an msafd socket. */
+  if (index < 0) {
+    return INVALID_SOCKET;
+  }
+
+  /* If we didn't (try) to create a peer socket yet, try to make one. Don't try
+   * again if the peer socket creation failed earlier for the same protocol. */
+  peer_socket = loop->poll_peer_sockets[index];
+  if (peer_socket == 0) {
+    peer_socket = uv__fast_poll_create_peer_socket(loop->iocp, protocol_info);
+    loop->poll_peer_sockets[index] = peer_socket;
+  }
+
+  return peer_socket;
+}
+
+
+static DWORD WINAPI uv__slow_poll_thread_proc(void* arg) {
+  uv_req_t* req = (uv_req_t*) arg;
+  uv_poll_t* handle = (uv_poll_t*) req->data;
+  unsigned char reported_events;
+  int r;
+  uv_single_fd_set_t rfds, wfds, efds;
+  struct timeval timeout;
+
+  assert(handle->type == UV_POLL);
+  assert(req->type == UV_POLL_REQ);
+
+  if (handle->events & UV_READABLE) {
+    rfds.fd_count = 1;
+    rfds.fd_array[0] = handle->socket;
+  } else {
+    rfds.fd_count = 0;
+  }
+
+  if (handle->events & UV_WRITABLE) {
+    wfds.fd_count = 1;
+    wfds.fd_array[0] = handle->socket;
+    efds.fd_count = 1;
+    efds.fd_array[0] = handle->socket;
+  } else {
+    wfds.fd_count = 0;
+    efds.fd_count = 0;
+  }
+
+  /* Make the select() time out after 3 minutes. If select() hangs because the
+   * user closed the socket, we will at least not hang indefinitely. */
+  timeout.tv_sec = 3 * 60;
+  timeout.tv_usec = 0;
+
+  r = select(1, (fd_set*) &rfds, (fd_set*) &wfds, (fd_set*) &efds, &timeout);
+  if (r == SOCKET_ERROR) {
+    /* Queue this req, reporting an error. */
+    SET_REQ_ERROR(&handle->poll_req_1, WSAGetLastError());
+    POST_COMPLETION_FOR_REQ(handle->loop, req);
+    return 0;
+  }
+
+  reported_events = 0;
+
+  if (r > 0) {
+    if (rfds.fd_count > 0) {
+      assert(rfds.fd_count == 1);
+      assert(rfds.fd_array[0] == handle->socket);
+      reported_events |= UV_READABLE;
+    }
+
+    if (wfds.fd_count > 0) {
+      assert(wfds.fd_count == 1);
+      assert(wfds.fd_array[0] == handle->socket);
+      reported_events |= UV_WRITABLE;
+    } else if (efds.fd_count > 0) {
+      assert(efds.fd_count == 1);
+      assert(efds.fd_array[0] == handle->socket);
+      reported_events |= UV_WRITABLE;
+    }
+  }
+
+  SET_REQ_SUCCESS(req);
+  req->u.io.overlapped.InternalHigh = (DWORD) reported_events;
+  POST_COMPLETION_FOR_REQ(handle->loop, req);
+
+  return 0;
+}
+
+
+static void uv__slow_poll_submit_poll_req(uv_loop_t* loop, uv_poll_t* handle) {
+  uv_req_t* req;
+
+  /* Find a yet unsubmitted req to submit. */
+  if (handle->submitted_events_1 == 0) {
+    req = &handle->poll_req_1;
+    handle->submitted_events_1 = handle->events;
+    handle->mask_events_1 = 0;
+    handle->mask_events_2 = handle->events;
+  } else if (handle->submitted_events_2 == 0) {
+    req = &handle->poll_req_2;
+    handle->submitted_events_2 = handle->events;
+    handle->mask_events_1 = handle->events;
+    handle->mask_events_2 = 0;
+  } else {
+    assert(0);
+    return;
+  }
+
+  if (!QueueUserWorkItem(uv__slow_poll_thread_proc,
+                         (void*) req,
+                         WT_EXECUTELONGFUNCTION)) {
+    /* Make this req pending, reporting an error. */
+    SET_REQ_ERROR(req, GetLastError());
+    uv__insert_pending_req(loop, req);
+  }
+}
+
+
+
+static void uv__slow_poll_process_poll_req(uv_loop_t* loop, uv_poll_t* handle,
+    uv_req_t* req) {
+  unsigned char mask_events;
+  int err;
+
+  if (req == &handle->poll_req_1) {
+    handle->submitted_events_1 = 0;
+    mask_events = handle->mask_events_1;
+  } else if (req == &handle->poll_req_2) {
+    handle->submitted_events_2 = 0;
+    mask_events = handle->mask_events_2;
+  } else {
+    assert(0);
+    return;
+  }
+
+  if (!REQ_SUCCESS(req)) {
+    /* Error. */
+    if (handle->events != 0) {
+      err = GET_REQ_ERROR(req);
+      handle->events = 0; /* Stop the watcher */
+      handle->poll_cb(handle, uv_translate_sys_error(err), 0);
+    }
+  } else {
+    /* Got some events. */
+    int events = req->u.io.overlapped.InternalHigh & handle->events & ~mask_events;
+    if (events != 0) {
+      handle->poll_cb(handle, 0, events);
+    }
+  }
+
+  if ((handle->events & ~(handle->submitted_events_1 |
+      handle->submitted_events_2)) != 0) {
+    uv__slow_poll_submit_poll_req(loop, handle);
+  } else if ((handle->flags & UV_HANDLE_CLOSING) &&
+             handle->submitted_events_1 == 0 &&
+             handle->submitted_events_2 == 0) {
+    uv__want_endgame(loop, (uv_handle_t*) handle);
+  }
+}
+
+
+int uv_poll_init(uv_loop_t* loop, uv_poll_t* handle, int fd) {
+  return uv_poll_init_socket(loop, handle, (SOCKET) uv__get_osfhandle(fd));
+}
+
+
+int uv_poll_init_socket(uv_loop_t* loop, uv_poll_t* handle,
+    uv_os_sock_t socket) {
+  WSAPROTOCOL_INFOW protocol_info;
+  int len;
+  SOCKET peer_socket, base_socket;
+  DWORD bytes;
+  DWORD yes = 1;
+
+  /* Set the socket to nonblocking mode */
+  if (ioctlsocket(socket, FIONBIO, &yes) == SOCKET_ERROR)
+    return uv_translate_sys_error(WSAGetLastError());
+
+/* Try to obtain a base handle for the socket. This increases this chances that
+ * we find an AFD handle and are able to use the fast poll mechanism. This will
+ * always fail on windows XP/2k3, since they don't support the. SIO_BASE_HANDLE
+ * ioctl. */
+#ifndef NDEBUG
+  base_socket = INVALID_SOCKET;
+#endif
+
+  if (WSAIoctl(socket,
+               SIO_BASE_HANDLE,
+               NULL,
+               0,
+               &base_socket,
+               sizeof base_socket,
+               &bytes,
+               NULL,
+               NULL) == 0) {
+    assert(base_socket != 0 && base_socket != INVALID_SOCKET);
+    socket = base_socket;
+  }
+
+  uv__handle_init(loop, (uv_handle_t*) handle, UV_POLL);
+  handle->socket = socket;
+  handle->events = 0;
+
+  /* Obtain protocol information about the socket. */
+  len = sizeof protocol_info;
+  if (getsockopt(socket,
+                 SOL_SOCKET,
+                 SO_PROTOCOL_INFOW,
+                 (char*) &protocol_info,
+                 &len) != 0) {
+    return uv_translate_sys_error(WSAGetLastError());
+  }
+
+  /* Get the peer socket that is needed to enable fast poll. If the returned
+   * value is NULL, the protocol is not implemented by MSAFD and we'll have to
+   * use slow mode. */
+  peer_socket = uv__fast_poll_get_peer_socket(loop, &protocol_info);
+
+  if (peer_socket != INVALID_SOCKET) {
+    /* Initialize fast poll specific fields. */
+    handle->peer_socket = peer_socket;
+  } else {
+    /* Initialize slow poll specific fields. */
+    handle->flags |= UV_HANDLE_POLL_SLOW;
+  }
+
+  /* Initialize 2 poll reqs. */
+  handle->submitted_events_1 = 0;
+  UV_REQ_INIT(&handle->poll_req_1, UV_POLL_REQ);
+  handle->poll_req_1.data = handle;
+
+  handle->submitted_events_2 = 0;
+  UV_REQ_INIT(&handle->poll_req_2, UV_POLL_REQ);
+  handle->poll_req_2.data = handle;
+
+  return 0;
+}
+
+
+static int uv__poll_set(uv_poll_t* handle, int events, uv_poll_cb cb) {
+  int submitted_events;
+
+  assert(handle->type == UV_POLL);
+  assert(!(handle->flags & UV_HANDLE_CLOSING));
+  assert((events & ~(UV_READABLE | UV_WRITABLE | UV_DISCONNECT |
+                     UV_PRIORITIZED)) == 0);
+
+  handle->events = events;
+  handle->poll_cb = cb;
+
+  if (handle->events == 0) {
+    uv__handle_stop(handle);
+    return 0;
+  }
+
+  uv__handle_start(handle);
+  submitted_events = handle->submitted_events_1 | handle->submitted_events_2;
+
+  if (handle->events & ~submitted_events) {
+    if (handle->flags & UV_HANDLE_POLL_SLOW) {
+      uv__slow_poll_submit_poll_req(handle->loop, handle);
+    } else {
+      uv__fast_poll_submit_poll_req(handle->loop, handle);
+    }
+  }
+
+  return 0;
+}
+
+
+int uv_poll_start(uv_poll_t* handle, int events, uv_poll_cb cb) {
+  return uv__poll_set(handle, events, cb);
+}
+
+
+int uv_poll_stop(uv_poll_t* handle) {
+  return uv__poll_set(handle, 0, handle->poll_cb);
+}
+
+
+void uv__process_poll_req(uv_loop_t* loop, uv_poll_t* handle, uv_req_t* req) {
+  if (!(handle->flags & UV_HANDLE_POLL_SLOW)) {
+    uv__fast_poll_process_poll_req(loop, handle, req);
+  } else {
+    uv__slow_poll_process_poll_req(loop, handle, req);
+  }
+}
+
+
+int uv__poll_close(uv_loop_t* loop, uv_poll_t* handle) {
+  AFD_POLL_INFO afd_poll_info;
+  DWORD error;
+  int result;
+
+  handle->events = 0;
+  uv__handle_closing(handle);
+
+  if (handle->submitted_events_1 == 0 &&
+      handle->submitted_events_2 == 0) {
+    uv__want_endgame(loop, (uv_handle_t*) handle);
+    return 0;
+  }
+
+  if (handle->flags & UV_HANDLE_POLL_SLOW)
+    return 0;
+
+  /* Cancel outstanding poll requests by executing another, unique poll
+   * request that forces the outstanding ones to return. */
+  afd_poll_info.Exclusive = TRUE;
+  afd_poll_info.NumberOfHandles = 1;
+  afd_poll_info.Timeout.QuadPart = INT64_MAX;
+  afd_poll_info.Handles[0].Handle = (HANDLE) handle->socket;
+  afd_poll_info.Handles[0].Status = 0;
+  afd_poll_info.Handles[0].Events = AFD_POLL_ALL;
+
+  result = uv__msafd_poll(handle->socket,
+                          &afd_poll_info,
+                          uv__get_afd_poll_info_dummy(),
+                          uv__get_overlapped_dummy());
+
+  if (result == SOCKET_ERROR) {
+    error = WSAGetLastError();
+    if (error != WSA_IO_PENDING)
+      return uv_translate_sys_error(error);
+  }
+
+  return 0;
+}
+
+
+void uv__poll_endgame(uv_loop_t* loop, uv_poll_t* handle) {
+  assert(handle->flags & UV_HANDLE_CLOSING);
+  assert(!(handle->flags & UV_HANDLE_CLOSED));
+
+  assert(handle->submitted_events_1 == 0);
+  assert(handle->submitted_events_2 == 0);
+
+  uv__handle_close(handle);
+}
diff --git a/wpinet/src/main/native/thirdparty/libuv/src/win/process-stdio.cpp b/wpinet/src/main/native/thirdparty/libuv/src/win/process-stdio.cpp
new file mode 100644
index 0000000..0db3572
--- /dev/null
+++ b/wpinet/src/main/native/thirdparty/libuv/src/win/process-stdio.cpp
@@ -0,0 +1,416 @@
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include <assert.h>
+#include <io.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "uv.h"
+#include "internal.h"
+#include "handle-inl.h"
+
+
+/*
+ * The `child_stdio_buffer` buffer has the following layout:
+ *   int number_of_fds
+ *   unsigned char crt_flags[number_of_fds]
+ *   HANDLE os_handle[number_of_fds]
+ */
+#define CHILD_STDIO_SIZE(count)                     \
+    (sizeof(int) +                                  \
+     sizeof(unsigned char) * (count) +              \
+     sizeof(uintptr_t) * (count))
+
+#define CHILD_STDIO_COUNT(buffer)                   \
+    *((unsigned int*) (buffer))
+
+#define CHILD_STDIO_CRT_FLAGS(buffer, fd)           \
+    *((unsigned char*) (buffer) + sizeof(int) + fd)
+
+#define CHILD_STDIO_HANDLE(buffer, fd)              \
+    *((HANDLE*) ((unsigned char*) (buffer) +        \
+                 sizeof(int) +                      \
+                 sizeof(unsigned char) *            \
+                 CHILD_STDIO_COUNT((buffer)) +      \
+                 sizeof(HANDLE) * (fd)))
+
+
+/* CRT file descriptor mode flags */
+#define FOPEN       0x01
+#define FEOFLAG     0x02
+#define FCRLF       0x04
+#define FPIPE       0x08
+#define FNOINHERIT  0x10
+#define FAPPEND     0x20
+#define FDEV        0x40
+#define FTEXT       0x80
+
+
+/*
+ * Clear the HANDLE_FLAG_INHERIT flag from all HANDLEs that were inherited
+ * the parent process. Don't check for errors - the stdio handles may not be
+ * valid, or may be closed already. There is no guarantee that this function
+ * does a perfect job.
+ */
+void uv_disable_stdio_inheritance(void) {
+  HANDLE handle;
+  STARTUPINFOW si;
+
+  /* Make the windows stdio handles non-inheritable. */
+  handle = GetStdHandle(STD_INPUT_HANDLE);
+  if (handle != NULL && handle != INVALID_HANDLE_VALUE)
+    SetHandleInformation(handle, HANDLE_FLAG_INHERIT, 0);
+
+  handle = GetStdHandle(STD_OUTPUT_HANDLE);
+  if (handle != NULL && handle != INVALID_HANDLE_VALUE)
+    SetHandleInformation(handle, HANDLE_FLAG_INHERIT, 0);
+
+  handle = GetStdHandle(STD_ERROR_HANDLE);
+  if (handle != NULL && handle != INVALID_HANDLE_VALUE)
+    SetHandleInformation(handle, HANDLE_FLAG_INHERIT, 0);
+
+  /* Make inherited CRT FDs non-inheritable. */
+  GetStartupInfoW(&si);
+  if (uv__stdio_verify(si.lpReserved2, si.cbReserved2))
+    uv__stdio_noinherit(si.lpReserved2);
+}
+
+
+static int uv__duplicate_handle(uv_loop_t* loop, HANDLE handle, HANDLE* dup) {
+  HANDLE current_process;
+
+
+  /* _get_osfhandle will sometimes return -2 in case of an error. This seems to
+   * happen when fd <= 2 and the process' corresponding stdio handle is set to
+   * NULL. Unfortunately DuplicateHandle will happily duplicate (HANDLE) -2, so
+   * this situation goes unnoticed until someone tries to use the duplicate.
+   * Therefore we filter out known-invalid handles here. */
+  if (handle == INVALID_HANDLE_VALUE ||
+      handle == NULL ||
+      handle == (HANDLE) -2) {
+    *dup = INVALID_HANDLE_VALUE;
+    return ERROR_INVALID_HANDLE;
+  }
+
+  current_process = GetCurrentProcess();
+
+  if (!DuplicateHandle(current_process,
+                       handle,
+                       current_process,
+                       dup,
+                       0,
+                       TRUE,
+                       DUPLICATE_SAME_ACCESS)) {
+    *dup = INVALID_HANDLE_VALUE;
+    return GetLastError();
+  }
+
+  return 0;
+}
+
+
+static int uv__duplicate_fd(uv_loop_t* loop, int fd, HANDLE* dup) {
+  HANDLE handle;
+
+  if (fd == -1) {
+    *dup = INVALID_HANDLE_VALUE;
+    return ERROR_INVALID_HANDLE;
+  }
+
+  handle = uv__get_osfhandle(fd);
+  return uv__duplicate_handle(loop, handle, dup);
+}
+
+
+int uv__create_nul_handle(HANDLE* handle_ptr,
+    DWORD access) {
+  HANDLE handle;
+  SECURITY_ATTRIBUTES sa;
+
+  sa.nLength = sizeof sa;
+  sa.lpSecurityDescriptor = NULL;
+  sa.bInheritHandle = TRUE;
+
+  handle = CreateFileW(L"NUL",
+                       access,
+                       FILE_SHARE_READ | FILE_SHARE_WRITE,
+                       &sa,
+                       OPEN_EXISTING,
+                       0,
+                       NULL);
+  if (handle == INVALID_HANDLE_VALUE) {
+    return GetLastError();
+  }
+
+  *handle_ptr = handle;
+  return 0;
+}
+
+
+int uv__stdio_create(uv_loop_t* loop,
+                     const uv_process_options_t* options,
+                     BYTE** buffer_ptr) {
+  BYTE* buffer;
+  int count, i;
+  int err;
+
+  count = options->stdio_count;
+
+  if (count < 0 || count > 255) {
+    /* Only support FDs 0-255 */
+    return ERROR_NOT_SUPPORTED;
+  } else if (count < 3) {
+    /* There should always be at least 3 stdio handles. */
+    count = 3;
+  }
+
+  /* Allocate the child stdio buffer */
+  buffer = (BYTE*) uv__malloc(CHILD_STDIO_SIZE(count));
+  if (buffer == NULL) {
+    return ERROR_OUTOFMEMORY;
+  }
+
+  /* Prepopulate the buffer with INVALID_HANDLE_VALUE handles so we can clean
+   * up on failure. */
+  CHILD_STDIO_COUNT(buffer) = count;
+  for (i = 0; i < count; i++) {
+    CHILD_STDIO_CRT_FLAGS(buffer, i) = 0;
+    CHILD_STDIO_HANDLE(buffer, i) = INVALID_HANDLE_VALUE;
+  }
+
+  for (i = 0; i < count; i++) {
+    uv_stdio_container_t fdopt;
+    if (i < options->stdio_count) {
+      fdopt = options->stdio[i];
+    } else {
+      fdopt.flags = UV_IGNORE;
+    }
+
+    switch (fdopt.flags & (UV_IGNORE | UV_CREATE_PIPE | UV_INHERIT_FD |
+            UV_INHERIT_STREAM)) {
+      case UV_IGNORE:
+        /* Starting a process with no stdin/stout/stderr can confuse it. So no
+         * matter what the user specified, we make sure the first three FDs are
+         * always open in their typical modes, e. g. stdin be readable and
+         * stdout/err should be writable. For FDs > 2, don't do anything - all
+         * handles in the stdio buffer are initialized with.
+         * INVALID_HANDLE_VALUE, which should be okay. */
+        if (i <= 2) {
+          DWORD access = (i == 0) ? FILE_GENERIC_READ :
+                                    FILE_GENERIC_WRITE | FILE_READ_ATTRIBUTES;
+
+          err = uv__create_nul_handle(&CHILD_STDIO_HANDLE(buffer, i),
+                                      access);
+          if (err)
+            goto error;
+
+          CHILD_STDIO_CRT_FLAGS(buffer, i) = FOPEN | FDEV;
+        }
+        break;
+
+      case UV_CREATE_PIPE: {
+        /* Create a pair of two connected pipe ends; one end is turned into an
+         * uv_pipe_t for use by the parent. The other one is given to the
+         * child. */
+        uv_pipe_t* parent_pipe = (uv_pipe_t*) fdopt.data.stream;
+        HANDLE child_pipe = INVALID_HANDLE_VALUE;
+
+        /* Create a new, connected pipe pair. stdio[i]. stream should point to
+         * an uninitialized, but not connected pipe handle. */
+        assert(fdopt.data.stream->type == UV_NAMED_PIPE);
+        assert(!(fdopt.data.stream->flags & UV_HANDLE_CONNECTION));
+        assert(!(fdopt.data.stream->flags & UV_HANDLE_PIPESERVER));
+
+        err = uv__create_stdio_pipe_pair(loop,
+                                         parent_pipe,
+                                         &child_pipe,
+                                         fdopt.flags);
+        if (err)
+          goto error;
+
+        CHILD_STDIO_HANDLE(buffer, i) = child_pipe;
+        CHILD_STDIO_CRT_FLAGS(buffer, i) = FOPEN | FPIPE;
+        break;
+      }
+
+      case UV_INHERIT_FD: {
+        /* Inherit a raw FD. */
+        HANDLE child_handle;
+
+        /* Make an inheritable duplicate of the handle. */
+        err = uv__duplicate_fd(loop, fdopt.data.fd, &child_handle);
+        if (err) {
+          /* If fdopt. data. fd is not valid and fd <= 2, then ignore the
+           * error. */
+          if (fdopt.data.fd <= 2 && err == ERROR_INVALID_HANDLE) {
+            CHILD_STDIO_CRT_FLAGS(buffer, i) = 0;
+            CHILD_STDIO_HANDLE(buffer, i) = INVALID_HANDLE_VALUE;
+            break;
+          }
+          goto error;
+        }
+
+        /* Figure out what the type is. */
+        switch (GetFileType(child_handle)) {
+          case FILE_TYPE_DISK:
+            CHILD_STDIO_CRT_FLAGS(buffer, i) = FOPEN;
+            break;
+
+          case FILE_TYPE_PIPE:
+            CHILD_STDIO_CRT_FLAGS(buffer, i) = FOPEN | FPIPE;
+            break;
+
+          case FILE_TYPE_CHAR:
+          case FILE_TYPE_REMOTE:
+            CHILD_STDIO_CRT_FLAGS(buffer, i) = FOPEN | FDEV;
+            break;
+
+          case FILE_TYPE_UNKNOWN:
+            if (GetLastError() != 0) {
+              err = GetLastError();
+              CloseHandle(child_handle);
+              goto error;
+            }
+            CHILD_STDIO_CRT_FLAGS(buffer, i) = FOPEN | FDEV;
+            break;
+
+          default:
+            assert(0);
+            return -1;
+        }
+
+        CHILD_STDIO_HANDLE(buffer, i) = child_handle;
+        break;
+      }
+
+      case UV_INHERIT_STREAM: {
+        /* Use an existing stream as the stdio handle for the child. */
+        HANDLE stream_handle, child_handle;
+        unsigned char crt_flags;
+        uv_stream_t* stream = fdopt.data.stream;
+
+        /* Leech the handle out of the stream. */
+        if (stream->type == UV_TTY) {
+          stream_handle = ((uv_tty_t*) stream)->handle;
+          crt_flags = FOPEN | FDEV;
+        } else if (stream->type == UV_NAMED_PIPE &&
+                   stream->flags & UV_HANDLE_CONNECTION) {
+          stream_handle = ((uv_pipe_t*) stream)->handle;
+          crt_flags = FOPEN | FPIPE;
+        } else {
+          stream_handle = INVALID_HANDLE_VALUE;
+          crt_flags = 0;
+        }
+
+        if (stream_handle == NULL ||
+            stream_handle == INVALID_HANDLE_VALUE) {
+          /* The handle is already closed, or not yet created, or the stream
+           * type is not supported. */
+          err = ERROR_NOT_SUPPORTED;
+          goto error;
+        }
+
+        /* Make an inheritable copy of the handle. */
+        err = uv__duplicate_handle(loop, stream_handle, &child_handle);
+        if (err)
+          goto error;
+
+        CHILD_STDIO_HANDLE(buffer, i) = child_handle;
+        CHILD_STDIO_CRT_FLAGS(buffer, i) = crt_flags;
+        break;
+      }
+
+      default:
+        assert(0);
+        return -1;
+    }
+  }
+
+  *buffer_ptr  = buffer;
+  return 0;
+
+ error:
+  uv__stdio_destroy(buffer);
+  return err;
+}
+
+
+void uv__stdio_destroy(BYTE* buffer) {
+  int i, count;
+
+  count = CHILD_STDIO_COUNT(buffer);
+  for (i = 0; i < count; i++) {
+    HANDLE handle = CHILD_STDIO_HANDLE(buffer, i);
+    if (handle != INVALID_HANDLE_VALUE) {
+      CloseHandle(handle);
+    }
+  }
+
+  uv__free(buffer);
+}
+
+
+void uv__stdio_noinherit(BYTE* buffer) {
+  int i, count;
+
+  count = CHILD_STDIO_COUNT(buffer);
+  for (i = 0; i < count; i++) {
+    HANDLE handle = CHILD_STDIO_HANDLE(buffer, i);
+    if (handle != INVALID_HANDLE_VALUE) {
+      SetHandleInformation(handle, HANDLE_FLAG_INHERIT, 0);
+    }
+  }
+}
+
+
+int uv__stdio_verify(BYTE* buffer, WORD size) {
+  unsigned int count;
+
+  /* Check the buffer pointer. */
+  if (buffer == NULL)
+    return 0;
+
+  /* Verify that the buffer is at least big enough to hold the count. */
+  if (size < CHILD_STDIO_SIZE(0))
+    return 0;
+
+  /* Verify if the count is within range. */
+  count = CHILD_STDIO_COUNT(buffer);
+  if (count > 256)
+    return 0;
+
+  /* Verify that the buffer size is big enough to hold info for N FDs. */
+  if (size < CHILD_STDIO_SIZE(count))
+    return 0;
+
+  return 1;
+}
+
+
+WORD uv__stdio_size(BYTE* buffer) {
+  return (WORD) CHILD_STDIO_SIZE(CHILD_STDIO_COUNT((buffer)));
+}
+
+
+HANDLE uv__stdio_handle(BYTE* buffer, int fd) {
+  return CHILD_STDIO_HANDLE(buffer, fd);
+}
diff --git a/wpinet/src/main/native/thirdparty/libuv/src/win/process.cpp b/wpinet/src/main/native/thirdparty/libuv/src/win/process.cpp
new file mode 100644
index 0000000..8e7835a
--- /dev/null
+++ b/wpinet/src/main/native/thirdparty/libuv/src/win/process.cpp
@@ -0,0 +1,1284 @@
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#define _CRT_NONSTDC_NO_WARNINGS
+
+#include <assert.h>
+#include <io.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <limits.h>
+#include <wchar.h>
+#include <malloc.h>    /* alloca */
+
+#include "uv.h"
+#include "internal.h"
+#include "handle-inl.h"
+#include "req-inl.h"
+
+#define SIGKILL         9
+
+
+typedef struct env_var {
+  const WCHAR* const wide;
+  const WCHAR* const wide_eq;
+  const size_t len; /* including null or '=' */
+} env_var_t;
+
+#define E_V(str) { L##str, L##str L"=", sizeof(str) }
+
+static const env_var_t required_vars[] = { /* keep me sorted */
+  E_V("HOMEDRIVE"),
+  E_V("HOMEPATH"),
+  E_V("LOGONSERVER"),
+  E_V("PATH"),
+  E_V("SYSTEMDRIVE"),
+  E_V("SYSTEMROOT"),
+  E_V("TEMP"),
+  E_V("USERDOMAIN"),
+  E_V("USERNAME"),
+  E_V("USERPROFILE"),
+  E_V("WINDIR"),
+};
+
+
+static HANDLE uv_global_job_handle_;
+static uv_once_t uv_global_job_handle_init_guard_ = UV_ONCE_INIT;
+
+
+static void uv__init_global_job_handle(void) {
+  /* Create a job object and set it up to kill all contained processes when
+   * it's closed. Since this handle is made non-inheritable and we're not
+   * giving it to anyone, we're the only process holding a reference to it.
+   * That means that if this process exits it is closed and all the processes
+   * it contains are killed. All processes created with uv_spawn that are not
+   * spawned with the UV_PROCESS_DETACHED flag are assigned to this job.
+   *
+   * We're setting the JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK flag so only the
+   * processes that we explicitly add are affected, and *their* subprocesses
+   * are not. This ensures that our child processes are not limited in their
+   * ability to use job control on Windows versions that don't deal with
+   * nested jobs (prior to Windows 8 / Server 2012). It also lets our child
+   * processes created detached processes without explicitly breaking away
+   * from job control (which uv_spawn doesn't, either).
+   */
+  SECURITY_ATTRIBUTES attr;
+  JOBOBJECT_EXTENDED_LIMIT_INFORMATION info;
+
+  memset(&attr, 0, sizeof attr);
+  attr.bInheritHandle = FALSE;
+
+  memset(&info, 0, sizeof info);
+  info.BasicLimitInformation.LimitFlags =
+      JOB_OBJECT_LIMIT_BREAKAWAY_OK |
+      JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK |
+      JOB_OBJECT_LIMIT_DIE_ON_UNHANDLED_EXCEPTION |
+      JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
+
+  uv_global_job_handle_ = CreateJobObjectW(&attr, NULL);
+  if (uv_global_job_handle_ == NULL)
+    uv_fatal_error(GetLastError(), "CreateJobObjectW");
+
+  if (!SetInformationJobObject(uv_global_job_handle_,
+                               JobObjectExtendedLimitInformation,
+                               &info,
+                               sizeof info))
+    uv_fatal_error(GetLastError(), "SetInformationJobObject");
+}
+
+
+static int uv__utf8_to_utf16_alloc(const char* s, WCHAR** ws_ptr) {
+  int ws_len, r;
+  WCHAR* ws;
+
+  ws_len = MultiByteToWideChar(CP_UTF8,
+                               0,
+                               s,
+                               -1,
+                               NULL,
+                               0);
+  if (ws_len <= 0) {
+    return GetLastError();
+  }
+
+  ws = (WCHAR*) uv__malloc(ws_len * sizeof(WCHAR));
+  if (ws == NULL) {
+    return ERROR_OUTOFMEMORY;
+  }
+
+  r = MultiByteToWideChar(CP_UTF8,
+                          0,
+                          s,
+                          -1,
+                          ws,
+                          ws_len);
+  assert(r == ws_len);
+
+  *ws_ptr = ws;
+  return 0;
+}
+
+
+static void uv__process_init(uv_loop_t* loop, uv_process_t* handle) {
+  uv__handle_init(loop, (uv_handle_t*) handle, UV_PROCESS);
+  handle->exit_cb = NULL;
+  handle->pid = 0;
+  handle->exit_signal = 0;
+  handle->wait_handle = INVALID_HANDLE_VALUE;
+  handle->process_handle = INVALID_HANDLE_VALUE;
+  handle->child_stdio_buffer = NULL;
+  handle->exit_cb_pending = 0;
+
+  UV_REQ_INIT(&handle->exit_req, UV_PROCESS_EXIT);
+  handle->exit_req.data = handle;
+}
+
+
+/*
+ * Path search functions
+ */
+
+/*
+ * Helper function for search_path
+ */
+static WCHAR* search_path_join_test(const WCHAR* dir,
+                                    size_t dir_len,
+                                    const WCHAR* name,
+                                    size_t name_len,
+                                    const WCHAR* ext,
+                                    size_t ext_len,
+                                    const WCHAR* cwd,
+                                    size_t cwd_len) {
+  WCHAR *result, *result_pos;
+  DWORD attrs;
+  if (dir_len > 2 &&
+      ((dir[0] == L'\\' || dir[0] == L'/') &&
+       (dir[1] == L'\\' || dir[1] == L'/'))) {
+    /* It's a UNC path so ignore cwd */
+    cwd_len = 0;
+  } else if (dir_len >= 1 && (dir[0] == L'/' || dir[0] == L'\\')) {
+    /* It's a full path without drive letter, use cwd's drive letter only */
+    cwd_len = 2;
+  } else if (dir_len >= 2 && dir[1] == L':' &&
+      (dir_len < 3 || (dir[2] != L'/' && dir[2] != L'\\'))) {
+    /* It's a relative path with drive letter (ext.g. D:../some/file)
+     * Replace drive letter in dir by full cwd if it points to the same drive,
+     * otherwise use the dir only.
+     */
+    if (cwd_len < 2 || _wcsnicmp(cwd, dir, 2) != 0) {
+      cwd_len = 0;
+    } else {
+      dir += 2;
+      dir_len -= 2;
+    }
+  } else if (dir_len > 2 && dir[1] == L':') {
+    /* It's an absolute path with drive letter
+     * Don't use the cwd at all
+     */
+    cwd_len = 0;
+  }
+
+  /* Allocate buffer for output */
+  result = result_pos = (WCHAR*)uv__malloc(sizeof(WCHAR) *
+      (cwd_len + 1 + dir_len + 1 + name_len + 1 + ext_len + 1));
+
+  /* Copy cwd */
+  wcsncpy(result_pos, cwd, cwd_len);
+  result_pos += cwd_len;
+
+  /* Add a path separator if cwd didn't end with one */
+  if (cwd_len && wcsrchr(L"\\/:", result_pos[-1]) == NULL) {
+    result_pos[0] = L'\\';
+    result_pos++;
+  }
+
+  /* Copy dir */
+  wcsncpy(result_pos, dir, dir_len);
+  result_pos += dir_len;
+
+  /* Add a separator if the dir didn't end with one */
+  if (dir_len && wcsrchr(L"\\/:", result_pos[-1]) == NULL) {
+    result_pos[0] = L'\\';
+    result_pos++;
+  }
+
+  /* Copy filename */
+  wcsncpy(result_pos, name, name_len);
+  result_pos += name_len;
+
+  if (ext_len) {
+    /* Add a dot if the filename didn't end with one */
+    if (name_len && result_pos[-1] != '.') {
+      result_pos[0] = L'.';
+      result_pos++;
+    }
+
+    /* Copy extension */
+    wcsncpy(result_pos, ext, ext_len);
+    result_pos += ext_len;
+  }
+
+  /* Null terminator */
+  result_pos[0] = L'\0';
+
+  attrs = GetFileAttributesW(result);
+
+  if (attrs != INVALID_FILE_ATTRIBUTES &&
+      !(attrs & FILE_ATTRIBUTE_DIRECTORY)) {
+    return result;
+  }
+
+  uv__free(result);
+  return NULL;
+}
+
+
+/*
+ * Helper function for search_path
+ */
+static WCHAR* path_search_walk_ext(const WCHAR *dir,
+                                   size_t dir_len,
+                                   const WCHAR *name,
+                                   size_t name_len,
+                                   WCHAR *cwd,
+                                   size_t cwd_len,
+                                   int name_has_ext) {
+  WCHAR* result;
+
+  /* If the name itself has a nonempty extension, try this extension first */
+  if (name_has_ext) {
+    result = search_path_join_test(dir, dir_len,
+                                   name, name_len,
+                                   L"", 0,
+                                   cwd, cwd_len);
+    if (result != NULL) {
+      return result;
+    }
+  }
+
+  /* Try .com extension */
+  result = search_path_join_test(dir, dir_len,
+                                 name, name_len,
+                                 L"com", 3,
+                                 cwd, cwd_len);
+  if (result != NULL) {
+    return result;
+  }
+
+  /* Try .exe extension */
+  result = search_path_join_test(dir, dir_len,
+                                 name, name_len,
+                                 L"exe", 3,
+                                 cwd, cwd_len);
+  if (result != NULL) {
+    return result;
+  }
+
+  return NULL;
+}
+
+
+/*
+ * search_path searches the system path for an executable filename -
+ * the windows API doesn't provide this as a standalone function nor as an
+ * option to CreateProcess.
+ *
+ * It tries to return an absolute filename.
+ *
+ * Furthermore, it tries to follow the semantics that cmd.exe, with this
+ * exception that PATHEXT environment variable isn't used. Since CreateProcess
+ * can start only .com and .exe files, only those extensions are tried. This
+ * behavior equals that of msvcrt's spawn functions.
+ *
+ * - Do not search the path if the filename already contains a path (either
+ *   relative or absolute).
+ *
+ * - If there's really only a filename, check the current directory for file,
+ *   then search all path directories.
+ *
+ * - If filename specified has *any* extension, search for the file with the
+ *   specified extension first.
+ *
+ * - If the literal filename is not found in a directory, try *appending*
+ *   (not replacing) .com first and then .exe.
+ *
+ * - The path variable may contain relative paths; relative paths are relative
+ *   to the cwd.
+ *
+ * - Directories in path may or may not end with a trailing backslash.
+ *
+ * - CMD does not trim leading/trailing whitespace from path/pathex entries
+ *   nor from the environment variables as a whole.
+ *
+ * - When cmd.exe cannot read a directory, it will just skip it and go on
+ *   searching. However, unlike posix-y systems, it will happily try to run a
+ *   file that is not readable/executable; if the spawn fails it will not
+ *   continue searching.
+ *
+ * UNC path support: we are dealing with UNC paths in both the path and the
+ * filename. This is a deviation from what cmd.exe does (it does not let you
+ * start a program by specifying an UNC path on the command line) but this is
+ * really a pointless restriction.
+ *
+ */
+static WCHAR* search_path(const WCHAR *file,
+                            WCHAR *cwd,
+                            const WCHAR *path) {
+  int file_has_dir;
+  WCHAR* result = NULL;
+  WCHAR *file_name_start;
+  WCHAR *dot;
+  const WCHAR *dir_start, *dir_end, *dir_path;
+  size_t dir_len;
+  int name_has_ext;
+
+  size_t file_len = wcslen(file);
+  size_t cwd_len = wcslen(cwd);
+
+  /* If the caller supplies an empty filename,
+   * we're not gonna return c:\windows\.exe -- GFY!
+   */
+  if (file_len == 0
+      || (file_len == 1 && file[0] == L'.')) {
+    return NULL;
+  }
+
+  /* Find the start of the filename so we can split the directory from the
+   * name. */
+  for (file_name_start = (WCHAR*)file + file_len;
+       file_name_start > file
+           && file_name_start[-1] != L'\\'
+           && file_name_start[-1] != L'/'
+           && file_name_start[-1] != L':';
+       file_name_start--);
+
+  file_has_dir = file_name_start != file;
+
+  /* Check if the filename includes an extension */
+  dot = wcschr(file_name_start, L'.');
+  name_has_ext = (dot != NULL && dot[1] != L'\0');
+
+  if (file_has_dir) {
+    /* The file has a path inside, don't use path */
+    result = path_search_walk_ext(
+        file, file_name_start - file,
+        file_name_start, file_len - (file_name_start - file),
+        cwd, cwd_len,
+        name_has_ext);
+
+  } else {
+    dir_end = path;
+
+    /* The file is really only a name; look in cwd first, then scan path */
+    result = path_search_walk_ext(L"", 0,
+                                  file, file_len,
+                                  cwd, cwd_len,
+                                  name_has_ext);
+
+    while (result == NULL) {
+      if (*dir_end == L'\0') {
+        break;
+      }
+
+      /* Skip the separator that dir_end now points to */
+      if (dir_end != path || *path == L';') {
+        dir_end++;
+      }
+
+      /* Next slice starts just after where the previous one ended */
+      dir_start = dir_end;
+
+      /* If path is quoted, find quote end */
+      if (*dir_start == L'"' || *dir_start == L'\'') {
+        dir_end = wcschr(dir_start + 1, *dir_start);
+        if (dir_end == NULL) {
+          dir_end = wcschr(dir_start, L'\0');
+        }
+      }
+      /* Slice until the next ; or \0 is found */
+      dir_end = wcschr(dir_end, L';');
+      if (dir_end == NULL) {
+        dir_end = wcschr(dir_start, L'\0');
+      }
+
+      /* If the slice is zero-length, don't bother */
+      if (dir_end - dir_start == 0) {
+        continue;
+      }
+
+      dir_path = dir_start;
+      dir_len = dir_end - dir_start;
+
+      /* Adjust if the path is quoted. */
+      if (dir_path[0] == '"' || dir_path[0] == '\'') {
+        ++dir_path;
+        --dir_len;
+      }
+
+      if (dir_path[dir_len - 1] == '"' || dir_path[dir_len - 1] == '\'') {
+        --dir_len;
+      }
+
+      result = path_search_walk_ext(dir_path, dir_len,
+                                    file, file_len,
+                                    cwd, cwd_len,
+                                    name_has_ext);
+    }
+  }
+
+  return result;
+}
+
+
+/*
+ * Quotes command line arguments
+ * Returns a pointer to the end (next char to be written) of the buffer
+ */
+WCHAR* quote_cmd_arg(const WCHAR *source, WCHAR *target) {
+  size_t len = wcslen(source);
+  size_t i;
+  int quote_hit;
+  WCHAR* start;
+
+  if (len == 0) {
+    /* Need double quotation for empty argument */
+    *(target++) = L'"';
+    *(target++) = L'"';
+    return target;
+  }
+
+  if (NULL == wcspbrk(source, L" \t\"")) {
+    /* No quotation needed */
+    wcsncpy(target, source, len);
+    target += len;
+    return target;
+  }
+
+  if (NULL == wcspbrk(source, L"\"\\")) {
+    /*
+     * No embedded double quotes or backlashes, so I can just wrap
+     * quote marks around the whole thing.
+     */
+    *(target++) = L'"';
+    wcsncpy(target, source, len);
+    target += len;
+    *(target++) = L'"';
+    return target;
+  }
+
+  /*
+   * Expected input/output:
+   *   input : hello"world
+   *   output: "hello\"world"
+   *   input : hello""world
+   *   output: "hello\"\"world"
+   *   input : hello\world
+   *   output: hello\world
+   *   input : hello\\world
+   *   output: hello\\world
+   *   input : hello\"world
+   *   output: "hello\\\"world"
+   *   input : hello\\"world
+   *   output: "hello\\\\\"world"
+   *   input : hello world\
+   *   output: "hello world\\"
+   */
+
+  *(target++) = L'"';
+  start = target;
+  quote_hit = 1;
+
+  for (i = len; i > 0; --i) {
+    *(target++) = source[i - 1];
+
+    if (quote_hit && source[i - 1] == L'\\') {
+      *(target++) = L'\\';
+    } else if(source[i - 1] == L'"') {
+      quote_hit = 1;
+      *(target++) = L'\\';
+    } else {
+      quote_hit = 0;
+    }
+  }
+  target[0] = L'\0';
+  wcsrev(start);
+  *(target++) = L'"';
+  return target;
+}
+
+
+int make_program_args(char** args, int verbatim_arguments, WCHAR** dst_ptr) {
+  char** arg;
+  WCHAR* dst = NULL;
+  WCHAR* temp_buffer = NULL;
+  size_t dst_len = 0;
+  size_t temp_buffer_len = 0;
+  WCHAR* pos;
+  int arg_count = 0;
+  int err = 0;
+
+  /* Count the required size. */
+  for (arg = args; *arg; arg++) {
+    DWORD arg_len;
+
+    arg_len = MultiByteToWideChar(CP_UTF8,
+                                  0,
+                                  *arg,
+                                  -1,
+                                  NULL,
+                                  0);
+    if (arg_len == 0) {
+      return GetLastError();
+    }
+
+    dst_len += arg_len;
+
+    if (arg_len > temp_buffer_len)
+      temp_buffer_len = arg_len;
+
+    arg_count++;
+  }
+
+  /* Adjust for potential quotes. Also assume the worst-case scenario that
+   * every character needs escaping, so we need twice as much space. */
+  dst_len = dst_len * 2 + arg_count * 2;
+
+  /* Allocate buffer for the final command line. */
+  dst = (WCHAR*) uv__malloc(dst_len * sizeof(WCHAR));
+  if (dst == NULL) {
+    err = ERROR_OUTOFMEMORY;
+    goto error;
+  }
+
+  /* Allocate temporary working buffer. */
+  temp_buffer = (WCHAR*) uv__malloc(temp_buffer_len * sizeof(WCHAR));
+  if (temp_buffer == NULL) {
+    err = ERROR_OUTOFMEMORY;
+    goto error;
+  }
+
+  pos = dst;
+  for (arg = args; *arg; arg++) {
+    DWORD arg_len;
+
+    /* Convert argument to wide char. */
+    arg_len = MultiByteToWideChar(CP_UTF8,
+                                  0,
+                                  *arg,
+                                  -1,
+                                  temp_buffer,
+                                  (int) (dst + dst_len - pos));
+    if (arg_len == 0) {
+      err = GetLastError();
+      goto error;
+    }
+
+    if (verbatim_arguments) {
+      /* Copy verbatim. */
+      wcscpy(pos, temp_buffer);
+      pos += arg_len - 1;
+    } else {
+      /* Quote/escape, if needed. */
+      pos = quote_cmd_arg(temp_buffer, pos);
+    }
+
+    *pos++ = *(arg + 1) ? L' ' : L'\0';
+  }
+
+  uv__free(temp_buffer);
+
+  *dst_ptr = dst;
+  return 0;
+
+error:
+  uv__free(dst);
+  uv__free(temp_buffer);
+  return err;
+}
+
+
+int env_strncmp(const wchar_t* a, int na, const wchar_t* b) {
+  const wchar_t* a_eq;
+  const wchar_t* b_eq;
+  wchar_t* A;
+  wchar_t* B;
+  int nb;
+  int r;
+
+  if (na < 0) {
+    a_eq = wcschr(a, L'=');
+    assert(a_eq);
+    na = (int)(long)(a_eq - a);
+  } else {
+    na--;
+  }
+  b_eq = wcschr(b, L'=');
+  assert(b_eq);
+  nb = b_eq - b;
+
+  A = (wchar_t*)alloca((na+1) * sizeof(wchar_t));
+  B = (wchar_t*)alloca((nb+1) * sizeof(wchar_t));
+
+  r = LCMapStringW(LOCALE_INVARIANT, LCMAP_UPPERCASE, a, na, A, na);
+  assert(r==na);
+  A[na] = L'\0';
+  r = LCMapStringW(LOCALE_INVARIANT, LCMAP_UPPERCASE, b, nb, B, nb);
+  assert(r==nb);
+  B[nb] = L'\0';
+
+  for (;;) {
+    wchar_t AA = *A++;
+    wchar_t BB = *B++;
+    if (AA < BB) {
+      return -1;
+    } else if (AA > BB) {
+      return 1;
+    } else if (!AA && !BB) {
+      return 0;
+    }
+  }
+}
+
+
+static int qsort_wcscmp(const void *a, const void *b) {
+  wchar_t* astr = *(wchar_t* const*)a;
+  wchar_t* bstr = *(wchar_t* const*)b;
+  return env_strncmp(astr, -1, bstr);
+}
+
+
+/*
+ * The way windows takes environment variables is different than what C does;
+ * Windows wants a contiguous block of null-terminated strings, terminated
+ * with an additional null.
+ *
+ * Windows has a few "essential" environment variables. winsock will fail
+ * to initialize if SYSTEMROOT is not defined; some APIs make reference to
+ * TEMP. SYSTEMDRIVE is probably also important. We therefore ensure that
+ * these get defined if the input environment block does not contain any
+ * values for them.
+ *
+ * Also add variables known to Cygwin to be required for correct
+ * subprocess operation in many cases:
+ * https://github.com/Alexpux/Cygwin/blob/b266b04fbbd3a595f02ea149e4306d3ab9b1fe3d/winsup/cygwin/environ.cc#L955
+ *
+ */
+int make_program_env(char* env_block[], WCHAR** dst_ptr) {
+  WCHAR* dst;
+  WCHAR* ptr;
+  char** env;
+  size_t env_len = 0;
+  int len;
+  size_t i;
+  DWORD var_size;
+  size_t env_block_count = 1; /* 1 for null-terminator */
+  WCHAR* dst_copy;
+  WCHAR** ptr_copy;
+  WCHAR** env_copy;
+  DWORD required_vars_value_len[ARRAY_SIZE(required_vars)];
+
+  /* first pass: determine size in UTF-16 */
+  for (env = env_block; *env; env++) {
+    int len;
+    if (strchr(*env, '=')) {
+      len = MultiByteToWideChar(CP_UTF8,
+                                0,
+                                *env,
+                                -1,
+                                NULL,
+                                0);
+      if (len <= 0) {
+        return GetLastError();
+      }
+      env_len += len;
+      env_block_count++;
+    }
+  }
+
+  /* second pass: copy to UTF-16 environment block */
+  dst_copy = (WCHAR*)uv__malloc(env_len * sizeof(WCHAR));
+  if (dst_copy == NULL && env_len > 0) {
+    return ERROR_OUTOFMEMORY;
+  }
+  env_copy = (WCHAR**)alloca(env_block_count * sizeof(WCHAR*));
+
+  ptr = dst_copy;
+  ptr_copy = env_copy;
+  for (env = env_block; *env; env++) {
+    if (strchr(*env, '=')) {
+      len = MultiByteToWideChar(CP_UTF8,
+                                0,
+                                *env,
+                                -1,
+                                ptr,
+                                (int) (env_len - (ptr - dst_copy)));
+      if (len <= 0) {
+        DWORD err = GetLastError();
+        uv__free(dst_copy);
+        return err;
+      }
+      *ptr_copy++ = ptr;
+      ptr += len;
+    }
+  }
+  *ptr_copy = NULL;
+  assert(env_len == 0 || env_len == (size_t) (ptr - dst_copy));
+
+  /* sort our (UTF-16) copy */
+  qsort(env_copy, env_block_count-1, sizeof(wchar_t*), qsort_wcscmp);
+
+  /* third pass: check for required variables */
+  for (ptr_copy = env_copy, i = 0; i < ARRAY_SIZE(required_vars); ) {
+    int cmp;
+    if (!*ptr_copy) {
+      cmp = -1;
+    } else {
+      cmp = env_strncmp(required_vars[i].wide_eq,
+                       required_vars[i].len,
+                        *ptr_copy);
+    }
+    if (cmp < 0) {
+      /* missing required var */
+      var_size = GetEnvironmentVariableW(required_vars[i].wide, NULL, 0);
+      required_vars_value_len[i] = var_size;
+      if (var_size != 0) {
+        env_len += required_vars[i].len;
+        env_len += var_size;
+      }
+      i++;
+    } else {
+      ptr_copy++;
+      if (cmp == 0)
+        i++;
+    }
+  }
+
+  /* final pass: copy, in sort order, and inserting required variables */
+  dst = (WCHAR*)uv__malloc((1+env_len) * sizeof(WCHAR));
+  if (!dst) {
+    uv__free(dst_copy);
+    return ERROR_OUTOFMEMORY;
+  }
+
+  for (ptr = dst, ptr_copy = env_copy, i = 0;
+       *ptr_copy || i < ARRAY_SIZE(required_vars);
+       ptr += len) {
+    int cmp;
+    if (i >= ARRAY_SIZE(required_vars)) {
+      cmp = 1;
+    } else if (!*ptr_copy) {
+      cmp = -1;
+    } else {
+      cmp = env_strncmp(required_vars[i].wide_eq,
+                        required_vars[i].len,
+                        *ptr_copy);
+    }
+    if (cmp < 0) {
+      /* missing required var */
+      len = required_vars_value_len[i];
+      if (len) {
+        wcscpy(ptr, required_vars[i].wide_eq);
+        ptr += required_vars[i].len;
+        var_size = GetEnvironmentVariableW(required_vars[i].wide,
+                                           ptr,
+                                           (int) (env_len - (ptr - dst)));
+        if (var_size != (DWORD) (len - 1)) { /* TODO: handle race condition? */
+          uv_fatal_error(GetLastError(), "GetEnvironmentVariableW");
+        }
+      }
+      i++;
+    } else {
+      /* copy var from env_block */
+      len = wcslen(*ptr_copy) + 1;
+      wmemcpy(ptr, *ptr_copy, len);
+      ptr_copy++;
+      if (cmp == 0)
+        i++;
+    }
+  }
+
+  /* Terminate with an extra NULL. */
+  assert(env_len == (size_t) (ptr - dst));
+  *ptr = L'\0';
+
+  uv__free(dst_copy);
+  *dst_ptr = dst;
+  return 0;
+}
+
+/*
+ * Attempt to find the value of the PATH environment variable in the child's
+ * preprocessed environment.
+ *
+ * If found, a pointer into `env` is returned. If not found, NULL is returned.
+ */
+static WCHAR* find_path(WCHAR *env) {
+  for (; env != NULL && *env != 0; env += wcslen(env) + 1) {
+    if ((env[0] == L'P' || env[0] == L'p') &&
+        (env[1] == L'A' || env[1] == L'a') &&
+        (env[2] == L'T' || env[2] == L't') &&
+        (env[3] == L'H' || env[3] == L'h') &&
+        (env[4] == L'=')) {
+      return &env[5];
+    }
+  }
+
+  return NULL;
+}
+
+/*
+ * Called on Windows thread-pool thread to indicate that
+ * a child process has exited.
+ */
+static void CALLBACK exit_wait_callback(void* data, BOOLEAN didTimeout) {
+  uv_process_t* process = (uv_process_t*) data;
+  uv_loop_t* loop = process->loop;
+
+  assert(didTimeout == FALSE);
+  assert(process);
+  assert(!process->exit_cb_pending);
+
+  process->exit_cb_pending = 1;
+
+  /* Post completed */
+  POST_COMPLETION_FOR_REQ(loop, &process->exit_req);
+}
+
+
+/* Called on main thread after a child process has exited. */
+void uv__process_proc_exit(uv_loop_t* loop, uv_process_t* handle) {
+  int64_t exit_code;
+  DWORD status;
+
+  assert(handle->exit_cb_pending);
+  handle->exit_cb_pending = 0;
+
+  /* If we're closing, don't call the exit callback. Just schedule a close
+   * callback now. */
+  if (handle->flags & UV_HANDLE_CLOSING) {
+    uv__want_endgame(loop, (uv_handle_t*) handle);
+    return;
+  }
+
+  /* Unregister from process notification. */
+  if (handle->wait_handle != INVALID_HANDLE_VALUE) {
+    UnregisterWait(handle->wait_handle);
+    handle->wait_handle = INVALID_HANDLE_VALUE;
+  }
+
+  /* Set the handle to inactive: no callbacks will be made after the exit
+   * callback. */
+  uv__handle_stop(handle);
+
+  if (GetExitCodeProcess(handle->process_handle, &status)) {
+    exit_code = status;
+  } else {
+    /* Unable to obtain the exit code. This should never happen. */
+    exit_code = uv_translate_sys_error(GetLastError());
+  }
+
+  /* Fire the exit callback. */
+  if (handle->exit_cb) {
+    handle->exit_cb(handle, exit_code, handle->exit_signal);
+  }
+}
+
+
+void uv__process_close(uv_loop_t* loop, uv_process_t* handle) {
+  uv__handle_closing(handle);
+
+  if (handle->wait_handle != INVALID_HANDLE_VALUE) {
+    /* This blocks until either the wait was cancelled, or the callback has
+     * completed. */
+    BOOL r = UnregisterWaitEx(handle->wait_handle, INVALID_HANDLE_VALUE);
+    if (!r) {
+      /* This should never happen, and if it happens, we can't recover... */
+      uv_fatal_error(GetLastError(), "UnregisterWaitEx");
+    }
+
+    handle->wait_handle = INVALID_HANDLE_VALUE;
+  }
+
+  if (!handle->exit_cb_pending) {
+    uv__want_endgame(loop, (uv_handle_t*)handle);
+  }
+}
+
+
+void uv__process_endgame(uv_loop_t* loop, uv_process_t* handle) {
+  assert(!handle->exit_cb_pending);
+  assert(handle->flags & UV_HANDLE_CLOSING);
+  assert(!(handle->flags & UV_HANDLE_CLOSED));
+
+  /* Clean-up the process handle. */
+  CloseHandle(handle->process_handle);
+
+  uv__handle_close(handle);
+}
+
+
+int uv_spawn(uv_loop_t* loop,
+             uv_process_t* process,
+             const uv_process_options_t* options) {
+  int i;
+  int err = 0;
+  WCHAR* path = NULL, *alloc_path = NULL;
+  BOOL result;
+  WCHAR* application_path = NULL, *application = NULL, *arguments = NULL,
+         *env = NULL, *cwd = NULL;
+  STARTUPINFOW startup;
+  PROCESS_INFORMATION info;
+  DWORD process_flags;
+
+  uv__process_init(loop, process);
+  process->exit_cb = options->exit_cb;
+
+  if (options->flags & (UV_PROCESS_SETGID | UV_PROCESS_SETUID)) {
+    return UV_ENOTSUP;
+  }
+
+  if (options->file == NULL ||
+      options->args == NULL) {
+    return UV_EINVAL;
+  }
+
+  assert(options->file != NULL);
+  assert(!(options->flags & ~(UV_PROCESS_DETACHED |
+                              UV_PROCESS_SETGID |
+                              UV_PROCESS_SETUID |
+                              UV_PROCESS_WINDOWS_HIDE |
+                              UV_PROCESS_WINDOWS_HIDE_CONSOLE |
+                              UV_PROCESS_WINDOWS_HIDE_GUI |
+                              UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS)));
+
+  err = uv__utf8_to_utf16_alloc(options->file, &application);
+  if (err)
+    goto done;
+
+  err = make_program_args(
+      options->args,
+      options->flags & UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS,
+      &arguments);
+  if (err)
+    goto done;
+
+  if (options->env) {
+     err = make_program_env(options->env, &env);
+     if (err)
+       goto done;
+  }
+
+  if (options->cwd) {
+    /* Explicit cwd */
+    err = uv__utf8_to_utf16_alloc(options->cwd, &cwd);
+    if (err)
+      goto done;
+
+  } else {
+    /* Inherit cwd */
+    DWORD cwd_len, r;
+
+    cwd_len = GetCurrentDirectoryW(0, NULL);
+    if (!cwd_len) {
+      err = GetLastError();
+      goto done;
+    }
+
+    cwd = (WCHAR*) uv__malloc(cwd_len * sizeof(WCHAR));
+    if (cwd == NULL) {
+      err = ERROR_OUTOFMEMORY;
+      goto done;
+    }
+
+    r = GetCurrentDirectoryW(cwd_len, cwd);
+    if (r == 0 || r >= cwd_len) {
+      err = GetLastError();
+      goto done;
+    }
+  }
+
+  /* Get PATH environment variable. */
+  path = find_path(env);
+  if (path == NULL) {
+    DWORD path_len, r;
+
+    path_len = GetEnvironmentVariableW(L"PATH", NULL, 0);
+    if (path_len == 0) {
+      err = GetLastError();
+      goto done;
+    }
+
+    alloc_path = (WCHAR*) uv__malloc(path_len * sizeof(WCHAR));
+    if (alloc_path == NULL) {
+      err = ERROR_OUTOFMEMORY;
+      goto done;
+    }
+    path = alloc_path;
+
+    r = GetEnvironmentVariableW(L"PATH", path, path_len);
+    if (r == 0 || r >= path_len) {
+      err = GetLastError();
+      goto done;
+    }
+  }
+
+  err = uv__stdio_create(loop, options, &process->child_stdio_buffer);
+  if (err)
+    goto done;
+
+  application_path = search_path(application,
+                                 cwd,
+                                 path);
+  if (application_path == NULL) {
+    /* Not found. */
+    err = ERROR_FILE_NOT_FOUND;
+    goto done;
+  }
+
+  startup.cb = sizeof(startup);
+  startup.lpReserved = NULL;
+  startup.lpDesktop = NULL;
+  startup.lpTitle = NULL;
+  startup.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
+
+  startup.cbReserved2 = uv__stdio_size(process->child_stdio_buffer);
+  startup.lpReserved2 = (BYTE*) process->child_stdio_buffer;
+
+  startup.hStdInput = uv__stdio_handle(process->child_stdio_buffer, 0);
+  startup.hStdOutput = uv__stdio_handle(process->child_stdio_buffer, 1);
+  startup.hStdError = uv__stdio_handle(process->child_stdio_buffer, 2);
+
+  process_flags = CREATE_UNICODE_ENVIRONMENT;
+
+  if ((options->flags & UV_PROCESS_WINDOWS_HIDE_CONSOLE) ||
+      (options->flags & UV_PROCESS_WINDOWS_HIDE)) {
+    /* Avoid creating console window if stdio is not inherited. */
+    for (i = 0; i < options->stdio_count; i++) {
+      if (options->stdio[i].flags & UV_INHERIT_FD)
+        break;
+      if (i == options->stdio_count - 1)
+        process_flags |= CREATE_NO_WINDOW;
+    }
+  }
+  if ((options->flags & UV_PROCESS_WINDOWS_HIDE_GUI) ||
+      (options->flags & UV_PROCESS_WINDOWS_HIDE)) {
+    /* Use SW_HIDE to avoid any potential process window. */
+    startup.wShowWindow = SW_HIDE;
+  } else {
+    startup.wShowWindow = SW_SHOWDEFAULT;
+  }
+
+  if (options->flags & UV_PROCESS_DETACHED) {
+    /* Note that we're not setting the CREATE_BREAKAWAY_FROM_JOB flag. That
+     * means that libuv might not let you create a fully daemonized process
+     * when run under job control. However the type of job control that libuv
+     * itself creates doesn't trickle down to subprocesses so they can still
+     * daemonize.
+     *
+     * A reason to not do this is that CREATE_BREAKAWAY_FROM_JOB makes the
+     * CreateProcess call fail if we're under job control that doesn't allow
+     * breakaway.
+     */
+    process_flags |= DETACHED_PROCESS | CREATE_NEW_PROCESS_GROUP;
+  }
+
+  if (!CreateProcessW(application_path,
+                     arguments,
+                     NULL,
+                     NULL,
+                     1,
+                     process_flags,
+                     env,
+                     cwd,
+                     &startup,
+                     &info)) {
+    /* CreateProcessW failed. */
+    err = GetLastError();
+    goto done;
+  }
+
+  /* Spawn succeeded. Beyond this point, failure is reported asynchronously. */
+
+  process->process_handle = info.hProcess;
+  process->pid = info.dwProcessId;
+
+  /* If the process isn't spawned as detached, assign to the global job object
+   * so windows will kill it when the parent process dies. */
+  if (!(options->flags & UV_PROCESS_DETACHED)) {
+    uv_once(&uv_global_job_handle_init_guard_, uv__init_global_job_handle);
+
+    if (!AssignProcessToJobObject(uv_global_job_handle_, info.hProcess)) {
+      /* AssignProcessToJobObject might fail if this process is under job
+       * control and the job doesn't have the
+       * JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK flag set, on a Windows version
+       * that doesn't support nested jobs.
+       *
+       * When that happens we just swallow the error and continue without
+       * establishing a kill-child-on-parent-exit relationship, otherwise
+       * there would be no way for libuv applications run under job control
+       * to spawn processes at all.
+       */
+      DWORD err = GetLastError();
+      if (err != ERROR_ACCESS_DENIED)
+        uv_fatal_error(err, "AssignProcessToJobObject");
+    }
+  }
+
+  /* Set IPC pid to all IPC pipes. */
+  for (i = 0; i < options->stdio_count; i++) {
+    const uv_stdio_container_t* fdopt = &options->stdio[i];
+    if (fdopt->flags & UV_CREATE_PIPE &&
+        fdopt->data.stream->type == UV_NAMED_PIPE &&
+        ((uv_pipe_t*) fdopt->data.stream)->ipc) {
+      ((uv_pipe_t*) fdopt->data.stream)->pipe.conn.ipc_remote_pid =
+          info.dwProcessId;
+    }
+  }
+
+  /* Setup notifications for when the child process exits. */
+  result = RegisterWaitForSingleObject(&process->wait_handle,
+      process->process_handle, exit_wait_callback, (void*)process, INFINITE,
+      WT_EXECUTEINWAITTHREAD | WT_EXECUTEONLYONCE);
+  if (!result) {
+    uv_fatal_error(GetLastError(), "RegisterWaitForSingleObject");
+  }
+
+  CloseHandle(info.hThread);
+
+  assert(!err);
+
+  /* Make the handle active. It will remain active until the exit callback is
+   * made or the handle is closed, whichever happens first. */
+  uv__handle_start(process);
+
+  /* Cleanup, whether we succeeded or failed. */
+ done:
+  uv__free(application);
+  uv__free(application_path);
+  uv__free(arguments);
+  uv__free(cwd);
+  uv__free(env);
+  uv__free(alloc_path);
+
+  if (process->child_stdio_buffer != NULL) {
+    /* Clean up child stdio handles. */
+    uv__stdio_destroy(process->child_stdio_buffer);
+    process->child_stdio_buffer = NULL;
+  }
+
+  return uv_translate_sys_error(err);
+}
+
+
+static int uv__kill(HANDLE process_handle, int signum) {
+  if (signum < 0 || signum >= NSIG) {
+    return UV_EINVAL;
+  }
+
+  switch (signum) {
+    case SIGTERM:
+    case SIGKILL:
+    case SIGINT: {
+      /* Unconditionally terminate the process. On Windows, killed processes
+       * normally return 1. */
+      DWORD status;
+      int err;
+
+      if (TerminateProcess(process_handle, 1))
+        return 0;
+
+      /* If the process already exited before TerminateProcess was called,.
+       * TerminateProcess will fail with ERROR_ACCESS_DENIED. */
+      err = GetLastError();
+      if (err == ERROR_ACCESS_DENIED &&
+          GetExitCodeProcess(process_handle, &status) &&
+          status != STILL_ACTIVE) {
+        return UV_ESRCH;
+      }
+
+      return uv_translate_sys_error(err);
+    }
+
+    case 0: {
+      /* Health check: is the process still alive? */
+      DWORD status;
+
+      if (!GetExitCodeProcess(process_handle, &status))
+        return uv_translate_sys_error(GetLastError());
+
+      if (status != STILL_ACTIVE)
+        return UV_ESRCH;
+
+      return 0;
+    }
+
+    default:
+      /* Unsupported signal. */
+      return UV_ENOSYS;
+  }
+}
+
+
+int uv_process_kill(uv_process_t* process, int signum) {
+  int err;
+
+  if (process->process_handle == INVALID_HANDLE_VALUE) {
+    return UV_EINVAL;
+  }
+
+  err = uv__kill(process->process_handle, signum);
+  if (err) {
+    return err;  /* err is already translated. */
+  }
+
+  process->exit_signal = signum;
+
+  return 0;
+}
+
+
+int uv_kill(int pid, int signum) {
+  int err;
+  HANDLE process_handle;
+
+  if (pid == 0) {
+    process_handle = GetCurrentProcess();
+  } else {
+    process_handle = OpenProcess(PROCESS_TERMINATE | PROCESS_QUERY_INFORMATION,
+                                 FALSE,
+                                 pid);
+  }
+
+  if (process_handle == NULL) {
+    err = GetLastError();
+    if (err == ERROR_INVALID_PARAMETER) {
+      return UV_ESRCH;
+    } else {
+      return uv_translate_sys_error(err);
+    }
+  }
+
+  err = uv__kill(process_handle, signum);
+  CloseHandle(process_handle);
+
+  return err;  /* err is already translated. */
+}
diff --git a/wpinet/src/main/native/thirdparty/libuv/src/win/req-inl.h b/wpinet/src/main/native/thirdparty/libuv/src/win/req-inl.h
new file mode 100644
index 0000000..9e20759
--- /dev/null
+++ b/wpinet/src/main/native/thirdparty/libuv/src/win/req-inl.h
@@ -0,0 +1,214 @@
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#ifndef UV_WIN_REQ_INL_H_
+#define UV_WIN_REQ_INL_H_
+
+#include <assert.h>
+
+#include "uv.h"
+#include "internal.h"
+
+
+#define SET_REQ_STATUS(req, status)                                     \
+   (req)->u.io.overlapped.Internal = (ULONG_PTR) (status)
+
+#define SET_REQ_ERROR(req, error)                                       \
+  SET_REQ_STATUS((req), NTSTATUS_FROM_WIN32((error)))
+
+/* Note: used open-coded in UV_REQ_INIT() because of a circular dependency
+ * between src/uv-common.h and src/win/internal.h.
+ */
+#define SET_REQ_SUCCESS(req)                                            \
+  SET_REQ_STATUS((req), STATUS_SUCCESS)
+
+#define GET_REQ_STATUS(req)                                             \
+  ((NTSTATUS) (req)->u.io.overlapped.Internal)
+
+#define REQ_SUCCESS(req)                                                \
+  (NT_SUCCESS(GET_REQ_STATUS((req))))
+
+#define GET_REQ_ERROR(req)                                              \
+  (pRtlNtStatusToDosError(GET_REQ_STATUS((req))))
+
+#define GET_REQ_SOCK_ERROR(req)                                         \
+  (uv__ntstatus_to_winsock_error(GET_REQ_STATUS((req))))
+
+
+#define REGISTER_HANDLE_REQ(loop, handle, req)                          \
+  do {                                                                  \
+    INCREASE_ACTIVE_COUNT((loop), (handle));                            \
+    uv__req_register((loop), (req));                                    \
+  } while (0)
+
+#define UNREGISTER_HANDLE_REQ(loop, handle, req)                        \
+  do {                                                                  \
+    DECREASE_ACTIVE_COUNT((loop), (handle));                            \
+    uv__req_unregister((loop), (req));                                  \
+  } while (0)
+
+
+#define UV_SUCCEEDED_WITHOUT_IOCP(result)                               \
+  ((result) && (handle->flags & UV_HANDLE_SYNC_BYPASS_IOCP))
+
+#define UV_SUCCEEDED_WITH_IOCP(result)                                  \
+  ((result) || (GetLastError() == ERROR_IO_PENDING))
+
+
+#define POST_COMPLETION_FOR_REQ(loop, req)                              \
+  if (!PostQueuedCompletionStatus((loop)->iocp,                         \
+                                  0,                                    \
+                                  0,                                    \
+                                  &((req)->u.io.overlapped))) {         \
+    uv_fatal_error(GetLastError(), "PostQueuedCompletionStatus");       \
+  }
+
+
+INLINE static uv_req_t* uv__overlapped_to_req(OVERLAPPED* overlapped) {
+  return CONTAINING_RECORD(overlapped, uv_req_t, u.io.overlapped);
+}
+
+
+INLINE static void uv__insert_pending_req(uv_loop_t* loop, uv_req_t* req) {
+  req->next_req = NULL;
+  if (loop->pending_reqs_tail) {
+#ifdef _DEBUG
+    /* Ensure the request is not already in the queue, or the queue
+     * will get corrupted.
+     */
+    uv_req_t* current = loop->pending_reqs_tail;
+    do {
+      assert(req != current);
+      current = current->next_req;
+    } while(current != loop->pending_reqs_tail);
+#endif
+
+    req->next_req = loop->pending_reqs_tail->next_req;
+    loop->pending_reqs_tail->next_req = req;
+    loop->pending_reqs_tail = req;
+  } else {
+    req->next_req = req;
+    loop->pending_reqs_tail = req;
+  }
+}
+
+
+#define DELEGATE_STREAM_REQ(loop, req, method, handle_at)                     \
+  do {                                                                        \
+    switch (((uv_handle_t*) (req)->handle_at)->type) {                        \
+      case UV_TCP:                                                            \
+        uv__process_tcp_##method##_req(loop,                                  \
+                                      (uv_tcp_t*) ((req)->handle_at),         \
+                                      req);                                   \
+        break;                                                                \
+                                                                              \
+      case UV_NAMED_PIPE:                                                     \
+        uv__process_pipe_##method##_req(loop,                                 \
+                                       (uv_pipe_t*) ((req)->handle_at),       \
+                                       req);                                  \
+        break;                                                                \
+                                                                              \
+      case UV_TTY:                                                            \
+        uv__process_tty_##method##_req(loop,                                  \
+                                      (uv_tty_t*) ((req)->handle_at),         \
+                                      req);                                   \
+        break;                                                                \
+                                                                              \
+      default:                                                                \
+        assert(0);                                                            \
+    }                                                                         \
+  } while (0)
+
+
+INLINE static void uv__process_reqs(uv_loop_t* loop) {
+  uv_req_t* req;
+  uv_req_t* first;
+  uv_req_t* next;
+
+  if (loop->pending_reqs_tail == NULL)
+    return;
+
+  first = loop->pending_reqs_tail->next_req;
+  next = first;
+  loop->pending_reqs_tail = NULL;
+
+  while (next != NULL) {
+    req = next;
+    next = req->next_req != first ? req->next_req : NULL;
+
+    switch (req->type) {
+      case UV_READ:
+        DELEGATE_STREAM_REQ(loop, req, read, data);
+        break;
+
+      case UV_WRITE:
+        DELEGATE_STREAM_REQ(loop, (uv_write_t*) req, write, handle);
+        break;
+
+      case UV_ACCEPT:
+        DELEGATE_STREAM_REQ(loop, req, accept, data);
+        break;
+
+      case UV_CONNECT:
+        DELEGATE_STREAM_REQ(loop, (uv_connect_t*) req, connect, handle);
+        break;
+
+      case UV_SHUTDOWN:
+        DELEGATE_STREAM_REQ(loop, (uv_shutdown_t*) req, shutdown, handle);
+        break;
+
+      case UV_UDP_RECV:
+        uv__process_udp_recv_req(loop, (uv_udp_t*) req->data, req);
+        break;
+
+      case UV_UDP_SEND:
+        uv__process_udp_send_req(loop,
+                                 ((uv_udp_send_t*) req)->handle,
+                                 (uv_udp_send_t*) req);
+        break;
+
+      case UV_WAKEUP:
+        uv__process_async_wakeup_req(loop, (uv_async_t*) req->data, req);
+        break;
+
+      case UV_SIGNAL_REQ:
+        uv__process_signal_req(loop, (uv_signal_t*) req->data, req);
+        break;
+
+      case UV_POLL_REQ:
+        uv__process_poll_req(loop, (uv_poll_t*) req->data, req);
+        break;
+
+      case UV_PROCESS_EXIT:
+        uv__process_proc_exit(loop, (uv_process_t*) req->data);
+        break;
+
+      case UV_FS_EVENT_REQ:
+        uv__process_fs_event_req(loop, req, (uv_fs_event_t*) req->data);
+        break;
+
+      default:
+        assert(0);
+    }
+  }
+}
+
+#endif /* UV_WIN_REQ_INL_H_ */
diff --git a/wpinet/src/main/native/thirdparty/libuv/src/win/signal.cpp b/wpinet/src/main/native/thirdparty/libuv/src/win/signal.cpp
new file mode 100644
index 0000000..8c79871
--- /dev/null
+++ b/wpinet/src/main/native/thirdparty/libuv/src/win/signal.cpp
@@ -0,0 +1,282 @@
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include <assert.h>
+#include <signal.h>
+
+#include "uv.h"
+#include "internal.h"
+#include "handle-inl.h"
+#include "req-inl.h"
+
+
+RB_HEAD(uv_signal_tree_s, uv_signal_s);
+
+static struct uv_signal_tree_s uv__signal_tree = RB_INITIALIZER(uv__signal_tree);
+static CRITICAL_SECTION uv__signal_lock;
+
+static BOOL WINAPI uv__signal_control_handler(DWORD type);
+
+int uv__signal_start(uv_signal_t* handle,
+                     uv_signal_cb signal_cb,
+                     int signum,
+                     int oneshot);
+
+void uv__signals_init(void) {
+  InitializeCriticalSection(&uv__signal_lock);
+  if (!SetConsoleCtrlHandler(uv__signal_control_handler, TRUE))
+    abort();
+}
+
+
+void uv__signal_cleanup(void) {
+  /* TODO(bnoordhuis) Undo effects of uv_signal_init()? */
+}
+
+
+static int uv__signal_compare(uv_signal_t* w1, uv_signal_t* w2) {
+  /* Compare signums first so all watchers with the same signnum end up
+   * adjacent. */
+  if (w1->signum < w2->signum) return -1;
+  if (w1->signum > w2->signum) return 1;
+
+  /* Sort by loop pointer, so we can easily look up the first item after
+   * { .signum = x, .loop = NULL }. */
+  if ((uintptr_t) w1->loop < (uintptr_t) w2->loop) return -1;
+  if ((uintptr_t) w1->loop > (uintptr_t) w2->loop) return 1;
+
+  if ((uintptr_t) w1 < (uintptr_t) w2) return -1;
+  if ((uintptr_t) w1 > (uintptr_t) w2) return 1;
+
+  return 0;
+}
+
+
+RB_GENERATE_STATIC(uv_signal_tree_s, uv_signal_s, tree_entry, uv__signal_compare)
+
+
+/*
+ * Dispatches signal {signum} to all active uv_signal_t watchers in all loops.
+ * Returns 1 if the signal was dispatched to any watcher, or 0 if there were
+ * no active signal watchers observing this signal.
+ */
+int uv__signal_dispatch(int signum) {
+  uv_signal_t lookup;
+  uv_signal_t* handle;
+  int dispatched;
+
+  dispatched = 0;
+
+  EnterCriticalSection(&uv__signal_lock);
+
+  lookup.signum = signum;
+  lookup.loop = NULL;
+
+  for (handle = RB_NFIND(uv_signal_tree_s, &uv__signal_tree, &lookup);
+       handle != NULL && handle->signum == signum;
+       handle = RB_NEXT(uv_signal_tree_s, &uv__signal_tree, handle)) {
+    unsigned long previous = InterlockedExchange(
+            (volatile LONG*) &handle->pending_signum, signum);
+
+    if (handle->flags & UV_SIGNAL_ONE_SHOT_DISPATCHED)
+      continue;
+
+    if (!previous) {
+      POST_COMPLETION_FOR_REQ(handle->loop, &handle->signal_req);
+    }
+
+    dispatched = 1;
+    if (handle->flags & UV_SIGNAL_ONE_SHOT)
+      handle->flags |= UV_SIGNAL_ONE_SHOT_DISPATCHED;
+  }
+
+  LeaveCriticalSection(&uv__signal_lock);
+
+  return dispatched;
+}
+
+
+static BOOL WINAPI uv__signal_control_handler(DWORD type) {
+  switch (type) {
+    case CTRL_C_EVENT:
+      return uv__signal_dispatch(SIGINT);
+
+    case CTRL_BREAK_EVENT:
+      return uv__signal_dispatch(SIGBREAK);
+
+    case CTRL_CLOSE_EVENT:
+      if (uv__signal_dispatch(SIGHUP)) {
+        /* Windows will terminate the process after the control handler
+         * returns. After that it will just terminate our process. Therefore
+         * block the signal handler so the main loop has some time to pick up
+         * the signal and do something for a few seconds. */
+        Sleep(INFINITE);
+        return TRUE;
+      }
+      return FALSE;
+
+    case CTRL_LOGOFF_EVENT:
+    case CTRL_SHUTDOWN_EVENT:
+      /* These signals are only sent to services. Services have their own
+       * notification mechanism, so there's no point in handling these. */
+
+    default:
+      /* We don't handle these. */
+      return FALSE;
+  }
+}
+
+
+int uv_signal_init(uv_loop_t* loop, uv_signal_t* handle) {
+  uv__handle_init(loop, (uv_handle_t*) handle, UV_SIGNAL);
+  handle->pending_signum = 0;
+  handle->signum = 0;
+  handle->signal_cb = NULL;
+
+  UV_REQ_INIT(&handle->signal_req, UV_SIGNAL_REQ);
+  handle->signal_req.data = handle;
+
+  return 0;
+}
+
+
+int uv_signal_stop(uv_signal_t* handle) {
+  uv_signal_t* removed_handle;
+
+  /* If the watcher wasn't started, this is a no-op. */
+  if (handle->signum == 0)
+    return 0;
+
+  EnterCriticalSection(&uv__signal_lock);
+
+  removed_handle = RB_REMOVE(uv_signal_tree_s, &uv__signal_tree, handle);
+  assert(removed_handle == handle);
+
+  LeaveCriticalSection(&uv__signal_lock);
+
+  handle->signum = 0;
+  uv__handle_stop(handle);
+
+  return 0;
+}
+
+
+int uv_signal_start(uv_signal_t* handle, uv_signal_cb signal_cb, int signum) {
+  return uv__signal_start(handle, signal_cb, signum, 0);
+}
+
+
+int uv_signal_start_oneshot(uv_signal_t* handle,
+                            uv_signal_cb signal_cb,
+                            int signum) {
+  return uv__signal_start(handle, signal_cb, signum, 1);
+}
+
+
+int uv__signal_start(uv_signal_t* handle,
+                            uv_signal_cb signal_cb,
+                            int signum,
+                            int oneshot) {
+  /* Test for invalid signal values. */
+  if (signum <= 0 || signum >= NSIG)
+    return UV_EINVAL;
+
+  /* Short circuit: if the signal watcher is already watching {signum} don't go
+   * through the process of deregistering and registering the handler.
+   * Additionally, this avoids pending signals getting lost in the (small) time
+   * frame that handle->signum == 0. */
+  if (signum == handle->signum) {
+    handle->signal_cb = signal_cb;
+    return 0;
+  }
+
+  /* If the signal handler was already active, stop it first. */
+  if (handle->signum != 0) {
+    int r = uv_signal_stop(handle);
+    /* uv_signal_stop is infallible. */
+    assert(r == 0);
+  }
+
+  EnterCriticalSection(&uv__signal_lock);
+
+  handle->signum = signum;
+  if (oneshot)
+    handle->flags |= UV_SIGNAL_ONE_SHOT;
+
+  RB_INSERT(uv_signal_tree_s, &uv__signal_tree, handle);
+
+  LeaveCriticalSection(&uv__signal_lock);
+
+  handle->signal_cb = signal_cb;
+  uv__handle_start(handle);
+
+  return 0;
+}
+
+
+void uv__process_signal_req(uv_loop_t* loop, uv_signal_t* handle,
+    uv_req_t* req) {
+  long dispatched_signum;
+
+  assert(handle->type == UV_SIGNAL);
+  assert(req->type == UV_SIGNAL_REQ);
+
+  dispatched_signum = InterlockedExchange(
+          (volatile LONG*) &handle->pending_signum, 0);
+  assert(dispatched_signum != 0);
+
+  /* Check if the pending signal equals the signum that we are watching for.
+   * These can get out of sync when the handler is stopped and restarted while
+   * the signal_req is pending. */
+  if (dispatched_signum == handle->signum)
+    handle->signal_cb(handle, dispatched_signum);
+
+  if (handle->flags & UV_SIGNAL_ONE_SHOT)
+    uv_signal_stop(handle);
+
+  if (handle->flags & UV_HANDLE_CLOSING) {
+    /* When it is closing, it must be stopped at this point. */
+    assert(handle->signum == 0);
+    uv__want_endgame(loop, (uv_handle_t*) handle);
+  }
+}
+
+
+void uv__signal_close(uv_loop_t* loop, uv_signal_t* handle) {
+  uv_signal_stop(handle);
+  uv__handle_closing(handle);
+
+  if (handle->pending_signum == 0) {
+    uv__want_endgame(loop, (uv_handle_t*) handle);
+  }
+}
+
+
+void uv__signal_endgame(uv_loop_t* loop, uv_signal_t* handle) {
+  assert(handle->flags & UV_HANDLE_CLOSING);
+  assert(!(handle->flags & UV_HANDLE_CLOSED));
+
+  assert(handle->signum == 0);
+  assert(handle->pending_signum == 0);
+
+  handle->flags |= UV_HANDLE_CLOSED;
+
+  uv__handle_close(handle);
+}
diff --git a/wpinet/src/main/native/thirdparty/libuv/src/win/snprintf.cpp b/wpinet/src/main/native/thirdparty/libuv/src/win/snprintf.cpp
new file mode 100644
index 0000000..776c0e3
--- /dev/null
+++ b/wpinet/src/main/native/thirdparty/libuv/src/win/snprintf.cpp
@@ -0,0 +1,42 @@
+/* Copyright the libuv project contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#if defined(_MSC_VER) && _MSC_VER < 1900
+
+#include <stdio.h>
+#include <stdarg.h>
+
+/* Emulate snprintf() on MSVC<2015, _snprintf() doesn't zero-terminate the buffer
+ * on overflow...
+ */
+int snprintf(char* buf, size_t len, const char* fmt, ...) {
+  int n;
+  va_list ap;
+  va_start(ap, fmt);
+
+  n = _vscprintf(fmt, ap);
+  vsnprintf_s(buf, len, _TRUNCATE, fmt, ap);
+
+  va_end(ap);
+  return n;
+}
+
+#endif
diff --git a/wpinet/src/main/native/thirdparty/libuv/src/win/stream-inl.h b/wpinet/src/main/native/thirdparty/libuv/src/win/stream-inl.h
new file mode 100644
index 0000000..91b1e78
--- /dev/null
+++ b/wpinet/src/main/native/thirdparty/libuv/src/win/stream-inl.h
@@ -0,0 +1,54 @@
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#ifndef UV_WIN_STREAM_INL_H_
+#define UV_WIN_STREAM_INL_H_
+
+#include <assert.h>
+
+#include "uv.h"
+#include "internal.h"
+#include "handle-inl.h"
+#include "req-inl.h"
+
+
+INLINE static void uv__stream_init(uv_loop_t* loop,
+                                   uv_stream_t* handle,
+                                   uv_handle_type type) {
+  uv__handle_init(loop, (uv_handle_t*) handle, type);
+  handle->write_queue_size = 0;
+  handle->activecnt = 0;
+  handle->stream.conn.shutdown_req = NULL;
+  handle->stream.conn.write_reqs_pending = 0;
+
+  UV_REQ_INIT(&handle->read_req, UV_READ);
+  handle->read_req.event_handle = NULL;
+  handle->read_req.wait_handle = INVALID_HANDLE_VALUE;
+  handle->read_req.data = handle;
+}
+
+
+INLINE static void uv__connection_init(uv_stream_t* handle) {
+  handle->flags |= UV_HANDLE_CONNECTION;
+}
+
+
+#endif /* UV_WIN_STREAM_INL_H_ */
diff --git a/wpinet/src/main/native/thirdparty/libuv/src/win/stream.cpp b/wpinet/src/main/native/thirdparty/libuv/src/win/stream.cpp
new file mode 100644
index 0000000..292bf58
--- /dev/null
+++ b/wpinet/src/main/native/thirdparty/libuv/src/win/stream.cpp
@@ -0,0 +1,253 @@
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include <assert.h>
+
+#include "uv.h"
+#include "internal.h"
+#include "handle-inl.h"
+#include "req-inl.h"
+
+
+int uv_listen(uv_stream_t* stream, int backlog, uv_connection_cb cb) {
+  int err;
+  if (uv__is_closing(stream)) {
+    return UV_EINVAL;
+  }
+  err = ERROR_INVALID_PARAMETER;
+  switch (stream->type) {
+    case UV_TCP:
+      err = uv__tcp_listen((uv_tcp_t*)stream, backlog, cb);
+      break;
+    case UV_NAMED_PIPE:
+      err = uv__pipe_listen((uv_pipe_t*)stream, backlog, cb);
+      break;
+    default:
+      assert(0);
+  }
+
+  return uv_translate_sys_error(err);
+}
+
+
+int uv_accept(uv_stream_t* server, uv_stream_t* client) {
+  int err;
+
+  err = ERROR_INVALID_PARAMETER;
+  switch (server->type) {
+    case UV_TCP:
+      err = uv__tcp_accept((uv_tcp_t*)server, (uv_tcp_t*)client);
+      break;
+    case UV_NAMED_PIPE:
+      err = uv__pipe_accept((uv_pipe_t*)server, client);
+      break;
+    default:
+      assert(0);
+  }
+
+  return uv_translate_sys_error(err);
+}
+
+
+int uv__read_start(uv_stream_t* handle,
+                   uv_alloc_cb alloc_cb,
+                   uv_read_cb read_cb) {
+  int err;
+
+  err = ERROR_INVALID_PARAMETER;
+  switch (handle->type) {
+    case UV_TCP:
+      err = uv__tcp_read_start((uv_tcp_t*)handle, alloc_cb, read_cb);
+      break;
+    case UV_NAMED_PIPE:
+      err = uv__pipe_read_start((uv_pipe_t*)handle, alloc_cb, read_cb);
+      break;
+    case UV_TTY:
+      err = uv__tty_read_start((uv_tty_t*) handle, alloc_cb, read_cb);
+      break;
+    default:
+      assert(0);
+  }
+
+  return uv_translate_sys_error(err);
+}
+
+
+int uv_read_stop(uv_stream_t* handle) {
+  int err;
+
+  if (!(handle->flags & UV_HANDLE_READING))
+    return 0;
+
+  err = 0;
+  if (handle->type == UV_TTY) {
+    err = uv__tty_read_stop((uv_tty_t*) handle);
+  } else if (handle->type == UV_NAMED_PIPE) {
+    uv__pipe_read_stop((uv_pipe_t*) handle);
+  } else {
+    handle->flags &= ~UV_HANDLE_READING;
+    DECREASE_ACTIVE_COUNT(handle->loop, handle);
+  }
+
+  return uv_translate_sys_error(err);
+}
+
+
+int uv_write(uv_write_t* req,
+             uv_stream_t* handle,
+             const uv_buf_t bufs[],
+             unsigned int nbufs,
+             uv_write_cb cb) {
+  uv_loop_t* loop = handle->loop;
+  int err;
+
+  if (!(handle->flags & UV_HANDLE_WRITABLE)) {
+    return UV_EPIPE;
+  }
+
+  err = ERROR_INVALID_PARAMETER;
+  switch (handle->type) {
+    case UV_TCP:
+      err = uv__tcp_write(loop, req, (uv_tcp_t*) handle, bufs, nbufs, cb);
+      break;
+    case UV_NAMED_PIPE:
+      err = uv__pipe_write(
+          loop, req, (uv_pipe_t*) handle, bufs, nbufs, NULL, cb);
+      break;
+    case UV_TTY:
+      err = uv__tty_write(loop, req, (uv_tty_t*) handle, bufs, nbufs, cb);
+      break;
+    default:
+      assert(0);
+  }
+
+  return uv_translate_sys_error(err);
+}
+
+
+int uv_write2(uv_write_t* req,
+              uv_stream_t* handle,
+              const uv_buf_t bufs[],
+              unsigned int nbufs,
+              uv_stream_t* send_handle,
+              uv_write_cb cb) {
+  uv_loop_t* loop = handle->loop;
+  int err;
+
+  if (send_handle == NULL) {
+    return uv_write(req, handle, bufs, nbufs, cb);
+  }
+
+  if (handle->type != UV_NAMED_PIPE || !((uv_pipe_t*) handle)->ipc) {
+    return UV_EINVAL;
+  } else if (!(handle->flags & UV_HANDLE_WRITABLE)) {
+    return UV_EPIPE;
+  }
+
+  err = uv__pipe_write(
+      loop, req, (uv_pipe_t*) handle, bufs, nbufs, send_handle, cb);
+  return uv_translate_sys_error(err);
+}
+
+
+int uv_try_write(uv_stream_t* stream,
+                 const uv_buf_t bufs[],
+                 unsigned int nbufs) {
+  if (stream->flags & UV_HANDLE_CLOSING)
+    return UV_EBADF;
+  if (!(stream->flags & UV_HANDLE_WRITABLE))
+    return UV_EPIPE;
+
+  switch (stream->type) {
+    case UV_TCP:
+      return uv__tcp_try_write((uv_tcp_t*) stream, bufs, nbufs);
+    case UV_TTY:
+      return uv__tty_try_write((uv_tty_t*) stream, bufs, nbufs);
+    case UV_NAMED_PIPE:
+      return UV_EAGAIN;
+    default:
+      assert(0);
+      return UV_ENOSYS;
+  }
+}
+
+
+int uv_try_write2(uv_stream_t* stream,
+                  const uv_buf_t bufs[],
+                  unsigned int nbufs,
+                  uv_stream_t* send_handle) {
+  if (send_handle != NULL)
+    return UV_EAGAIN;
+  return uv_try_write(stream, bufs, nbufs);
+}
+
+
+int uv_shutdown(uv_shutdown_t* req, uv_stream_t* handle, uv_shutdown_cb cb) {
+  uv_loop_t* loop = handle->loop;
+
+  if (!(handle->flags & UV_HANDLE_WRITABLE) ||
+      handle->flags & UV_HANDLE_SHUTTING ||
+      uv__is_closing(handle)) {
+    return UV_ENOTCONN;
+  }
+
+  UV_REQ_INIT(req, UV_SHUTDOWN);
+  req->handle = handle;
+  req->cb = cb;
+
+  handle->flags &= ~UV_HANDLE_WRITABLE;
+  handle->flags |= UV_HANDLE_SHUTTING;
+  handle->stream.conn.shutdown_req = req;
+  handle->reqs_pending++;
+  REGISTER_HANDLE_REQ(loop, handle, req);
+
+  if (handle->stream.conn.write_reqs_pending == 0) {
+    if (handle->type == UV_NAMED_PIPE)
+      uv__pipe_shutdown(loop, (uv_pipe_t*) handle, req);
+    else
+      uv__insert_pending_req(loop, (uv_req_t*) req);
+  }
+
+  return 0;
+}
+
+
+int uv_is_readable(const uv_stream_t* handle) {
+  return !!(handle->flags & UV_HANDLE_READABLE);
+}
+
+
+int uv_is_writable(const uv_stream_t* handle) {
+  return !!(handle->flags & UV_HANDLE_WRITABLE);
+}
+
+
+int uv_stream_set_blocking(uv_stream_t* handle, int blocking) {
+  if (handle->type != UV_NAMED_PIPE)
+    return UV_EINVAL;
+
+  if (blocking != 0)
+    handle->flags |= UV_HANDLE_BLOCKING_WRITES;
+  else
+    handle->flags &= ~UV_HANDLE_BLOCKING_WRITES;
+
+  return 0;
+}
diff --git a/wpinet/src/main/native/thirdparty/libuv/src/win/tcp.cpp b/wpinet/src/main/native/thirdparty/libuv/src/win/tcp.cpp
new file mode 100644
index 0000000..4cccee4
--- /dev/null
+++ b/wpinet/src/main/native/thirdparty/libuv/src/win/tcp.cpp
@@ -0,0 +1,1699 @@
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include <assert.h>
+#include <stdlib.h>
+
+#include "uv.h"
+#include "internal.h"
+#include "handle-inl.h"
+#include "stream-inl.h"
+#include "req-inl.h"
+
+
+/*
+ * Threshold of active tcp streams for which to preallocate tcp read buffers.
+ * (Due to node slab allocator performing poorly under this pattern,
+ *  the optimization is temporarily disabled (threshold=0).  This will be
+ *  revisited once node allocator is improved.)
+ */
+const unsigned int uv_active_tcp_streams_threshold = 0;
+
+/*
+ * Number of simultaneous pending AcceptEx calls.
+ */
+const unsigned int uv_simultaneous_server_accepts = 32;
+
+/* A zero-size buffer for use by uv_tcp_read */
+static char uv_zero_[] = "";
+
+static int uv__tcp_nodelay(uv_tcp_t* handle, SOCKET socket, int enable) {
+  if (setsockopt(socket,
+                 IPPROTO_TCP,
+                 TCP_NODELAY,
+                 (const char*)&enable,
+                 sizeof enable) == -1) {
+    return WSAGetLastError();
+  }
+  return 0;
+}
+
+
+static int uv__tcp_keepalive(uv_tcp_t* handle, SOCKET socket, int enable, unsigned int delay) {
+  if (setsockopt(socket,
+                 SOL_SOCKET,
+                 SO_KEEPALIVE,
+                 (const char*)&enable,
+                 sizeof enable) == -1) {
+    return WSAGetLastError();
+  }
+
+  if (enable && setsockopt(socket,
+                           IPPROTO_TCP,
+                           TCP_KEEPALIVE,
+                           (const char*)&delay,
+                           sizeof delay) == -1) {
+    return WSAGetLastError();
+  }
+
+  return 0;
+}
+
+
+static int uv__tcp_set_socket(uv_loop_t* loop,
+                              uv_tcp_t* handle,
+                              SOCKET socket,
+                              int family,
+                              int imported) {
+  DWORD yes = 1;
+  int non_ifs_lsp;
+  int err;
+
+  if (handle->socket != INVALID_SOCKET)
+    return UV_EBUSY;
+
+  /* Set the socket to nonblocking mode */
+  if (ioctlsocket(socket, FIONBIO, &yes) == SOCKET_ERROR) {
+    return WSAGetLastError();
+  }
+
+  /* Make the socket non-inheritable */
+  if (!SetHandleInformation((HANDLE) socket, HANDLE_FLAG_INHERIT, 0))
+    return GetLastError();
+
+  /* Associate it with the I/O completion port. Use uv_handle_t pointer as
+   * completion key. */
+  if (CreateIoCompletionPort((HANDLE)socket,
+                             loop->iocp,
+                             (ULONG_PTR)socket,
+                             0) == NULL) {
+    if (imported) {
+      handle->flags |= UV_HANDLE_EMULATE_IOCP;
+    } else {
+      return GetLastError();
+    }
+  }
+
+  if (family == AF_INET6) {
+    non_ifs_lsp = uv_tcp_non_ifs_lsp_ipv6;
+  } else {
+    non_ifs_lsp = uv_tcp_non_ifs_lsp_ipv4;
+  }
+
+  if (!(handle->flags & UV_HANDLE_EMULATE_IOCP) && !non_ifs_lsp) {
+    UCHAR sfcnm_flags =
+        FILE_SKIP_SET_EVENT_ON_HANDLE | FILE_SKIP_COMPLETION_PORT_ON_SUCCESS;
+    if (!SetFileCompletionNotificationModes((HANDLE) socket, sfcnm_flags))
+      return GetLastError();
+    handle->flags |= UV_HANDLE_SYNC_BYPASS_IOCP;
+  }
+
+  if (handle->flags & UV_HANDLE_TCP_NODELAY) {
+    err = uv__tcp_nodelay(handle, socket, 1);
+    if (err)
+      return err;
+  }
+
+  /* TODO: Use stored delay. */
+  if (handle->flags & UV_HANDLE_TCP_KEEPALIVE) {
+    err = uv__tcp_keepalive(handle, socket, 1, 60);
+    if (err)
+      return err;
+  }
+
+  handle->socket = socket;
+
+  if (family == AF_INET6) {
+    handle->flags |= UV_HANDLE_IPV6;
+  } else {
+    assert(!(handle->flags & UV_HANDLE_IPV6));
+  }
+
+  return 0;
+}
+
+
+int uv_tcp_init_ex(uv_loop_t* loop, uv_tcp_t* handle, unsigned int flags) {
+  int domain;
+
+  /* Use the lower 8 bits for the domain */
+  domain = flags & 0xFF;
+  if (domain != AF_INET && domain != AF_INET6 && domain != AF_UNSPEC)
+    return UV_EINVAL;
+
+  if (flags & ~0xFF)
+    return UV_EINVAL;
+
+  uv__stream_init(loop, (uv_stream_t*) handle, UV_TCP);
+  handle->tcp.serv.accept_reqs = NULL;
+  handle->tcp.serv.pending_accepts = NULL;
+  handle->socket = INVALID_SOCKET;
+  handle->reqs_pending = 0;
+  handle->tcp.serv.func_acceptex = NULL;
+  handle->tcp.conn.func_connectex = NULL;
+  handle->tcp.serv.processed_accepts = 0;
+  handle->delayed_error = 0;
+
+  /* If anything fails beyond this point we need to remove the handle from
+   * the handle queue, since it was added by uv__handle_init in uv__stream_init.
+   */
+
+  if (domain != AF_UNSPEC) {
+    SOCKET sock;
+    DWORD err;
+
+    sock = socket(domain, SOCK_STREAM, 0);
+    if (sock == INVALID_SOCKET) {
+      err = WSAGetLastError();
+      QUEUE_REMOVE(&handle->handle_queue);
+      return uv_translate_sys_error(err);
+    }
+
+    err = uv__tcp_set_socket(handle->loop, handle, sock, domain, 0);
+    if (err) {
+      closesocket(sock);
+      QUEUE_REMOVE(&handle->handle_queue);
+      return uv_translate_sys_error(err);
+    }
+
+  }
+
+  return 0;
+}
+
+
+int uv_tcp_init(uv_loop_t* loop, uv_tcp_t* handle) {
+  return uv_tcp_init_ex(loop, handle, AF_UNSPEC);
+}
+
+
+void uv__process_tcp_shutdown_req(uv_loop_t* loop, uv_tcp_t* stream, uv_shutdown_t *req) {
+  int err;
+
+  assert(req);
+  assert(stream->stream.conn.write_reqs_pending == 0);
+  assert(!(stream->flags & UV_HANDLE_SHUT));
+  assert(stream->flags & UV_HANDLE_CONNECTION);
+
+  stream->stream.conn.shutdown_req = NULL;
+  stream->flags &= ~UV_HANDLE_SHUTTING;
+  UNREGISTER_HANDLE_REQ(loop, stream, req);
+
+  err = 0;
+  if (stream->flags & UV_HANDLE_CLOSING)
+   /* The user destroyed the stream before we got to do the shutdown. */
+    err = UV_ECANCELED;
+  else if (shutdown(stream->socket, SD_SEND) == SOCKET_ERROR)
+    err = uv_translate_sys_error(WSAGetLastError());
+  else /* Success. */
+    stream->flags |= UV_HANDLE_SHUT;
+
+  if (req->cb)
+    req->cb(req, err);
+
+  DECREASE_PENDING_REQ_COUNT(stream);
+}
+
+
+void uv__tcp_endgame(uv_loop_t* loop, uv_tcp_t* handle) {
+  unsigned int i;
+  uv_tcp_accept_t* req;
+
+  assert(handle->flags & UV_HANDLE_CLOSING);
+  assert(handle->reqs_pending == 0);
+  assert(!(handle->flags & UV_HANDLE_CLOSED));
+  assert(handle->socket == INVALID_SOCKET);
+
+  if (!(handle->flags & UV_HANDLE_CONNECTION) && handle->tcp.serv.accept_reqs) {
+    if (handle->flags & UV_HANDLE_EMULATE_IOCP) {
+      for (i = 0; i < uv_simultaneous_server_accepts; i++) {
+        req = &handle->tcp.serv.accept_reqs[i];
+        if (req->wait_handle != INVALID_HANDLE_VALUE) {
+          UnregisterWait(req->wait_handle);
+          req->wait_handle = INVALID_HANDLE_VALUE;
+        }
+        if (req->event_handle != NULL) {
+          CloseHandle(req->event_handle);
+          req->event_handle = NULL;
+        }
+      }
+    }
+
+    uv__free(handle->tcp.serv.accept_reqs);
+    handle->tcp.serv.accept_reqs = NULL;
+  }
+
+  if (handle->flags & UV_HANDLE_CONNECTION &&
+      handle->flags & UV_HANDLE_EMULATE_IOCP) {
+    if (handle->read_req.wait_handle != INVALID_HANDLE_VALUE) {
+      UnregisterWait(handle->read_req.wait_handle);
+      handle->read_req.wait_handle = INVALID_HANDLE_VALUE;
+    }
+    if (handle->read_req.event_handle != NULL) {
+      CloseHandle(handle->read_req.event_handle);
+      handle->read_req.event_handle = NULL;
+    }
+  }
+
+  uv__handle_close(handle);
+  loop->active_tcp_streams--;
+}
+
+
+/* Unlike on Unix, here we don't set SO_REUSEADDR, because it doesn't just
+ * allow binding to addresses that are in use by sockets in TIME_WAIT, it
+ * effectively allows 'stealing' a port which is in use by another application.
+ *
+ * SO_EXCLUSIVEADDRUSE is also not good here because it does check all sockets,
+ * regardless of state, so we'd get an error even if the port is in use by a
+ * socket in TIME_WAIT state.
+ *
+ * See issue #1360.
+ *
+ */
+static int uv__tcp_try_bind(uv_tcp_t* handle,
+                            const struct sockaddr* addr,
+                            unsigned int addrlen,
+                            unsigned int flags) {
+  DWORD err;
+  int r;
+
+  if (handle->socket == INVALID_SOCKET) {
+    SOCKET sock;
+
+    /* Cannot set IPv6-only mode on non-IPv6 socket. */
+    if ((flags & UV_TCP_IPV6ONLY) && addr->sa_family != AF_INET6)
+      return ERROR_INVALID_PARAMETER;
+
+    sock = socket(addr->sa_family, SOCK_STREAM, 0);
+    if (sock == INVALID_SOCKET) {
+      return WSAGetLastError();
+    }
+
+    err = uv__tcp_set_socket(handle->loop, handle, sock, addr->sa_family, 0);
+    if (err) {
+      closesocket(sock);
+      return err;
+    }
+  }
+
+#ifdef IPV6_V6ONLY
+  if (addr->sa_family == AF_INET6) {
+    int on;
+
+    on = (flags & UV_TCP_IPV6ONLY) != 0;
+
+    /* TODO: how to handle errors? This may fail if there is no ipv4 stack
+     * available, or when run on XP/2003 which have no support for dualstack
+     * sockets. For now we're silently ignoring the error. */
+    setsockopt(handle->socket,
+               IPPROTO_IPV6,
+               IPV6_V6ONLY,
+               (const char*)&on,
+               sizeof on);
+  }
+#endif
+
+  r = bind(handle->socket, addr, addrlen);
+
+  if (r == SOCKET_ERROR) {
+    err = WSAGetLastError();
+    if (err == WSAEADDRINUSE) {
+      /* Some errors are not to be reported until connect() or listen() */
+      handle->delayed_error = err;
+    } else {
+      return err;
+    }
+  }
+
+  handle->flags |= UV_HANDLE_BOUND;
+
+  return 0;
+}
+
+
+static void CALLBACK post_completion(void* context, BOOLEAN timed_out) {
+  uv_req_t* req;
+  uv_tcp_t* handle;
+
+  req = (uv_req_t*) context;
+  assert(req != NULL);
+  handle = (uv_tcp_t*)req->data;
+  assert(handle != NULL);
+  assert(!timed_out);
+
+  if (!PostQueuedCompletionStatus(handle->loop->iocp,
+                                  req->u.io.overlapped.InternalHigh,
+                                  0,
+                                  &req->u.io.overlapped)) {
+    uv_fatal_error(GetLastError(), "PostQueuedCompletionStatus");
+  }
+}
+
+
+static void CALLBACK post_write_completion(void* context, BOOLEAN timed_out) {
+  uv_write_t* req;
+  uv_tcp_t* handle;
+
+  req = (uv_write_t*) context;
+  assert(req != NULL);
+  handle = (uv_tcp_t*)req->handle;
+  assert(handle != NULL);
+  assert(!timed_out);
+
+  if (!PostQueuedCompletionStatus(handle->loop->iocp,
+                                  req->u.io.overlapped.InternalHigh,
+                                  0,
+                                  &req->u.io.overlapped)) {
+    uv_fatal_error(GetLastError(), "PostQueuedCompletionStatus");
+  }
+}
+
+
+static void uv__tcp_queue_accept(uv_tcp_t* handle, uv_tcp_accept_t* req) {
+  uv_loop_t* loop = handle->loop;
+  BOOL success;
+  DWORD bytes;
+  SOCKET accept_socket;
+  short family;
+
+  assert(handle->flags & UV_HANDLE_LISTENING);
+  assert(req->accept_socket == INVALID_SOCKET);
+
+  /* choose family and extension function */
+  if (handle->flags & UV_HANDLE_IPV6) {
+    family = AF_INET6;
+  } else {
+    family = AF_INET;
+  }
+
+  /* Open a socket for the accepted connection. */
+  accept_socket = socket(family, SOCK_STREAM, 0);
+  if (accept_socket == INVALID_SOCKET) {
+    SET_REQ_ERROR(req, WSAGetLastError());
+    uv__insert_pending_req(loop, (uv_req_t*)req);
+    handle->reqs_pending++;
+    return;
+  }
+
+  /* Make the socket non-inheritable */
+  if (!SetHandleInformation((HANDLE) accept_socket, HANDLE_FLAG_INHERIT, 0)) {
+    SET_REQ_ERROR(req, GetLastError());
+    uv__insert_pending_req(loop, (uv_req_t*)req);
+    handle->reqs_pending++;
+    closesocket(accept_socket);
+    return;
+  }
+
+  /* Prepare the overlapped structure. */
+  memset(&(req->u.io.overlapped), 0, sizeof(req->u.io.overlapped));
+  if (handle->flags & UV_HANDLE_EMULATE_IOCP) {
+    assert(req->event_handle != NULL);
+    req->u.io.overlapped.hEvent = (HANDLE) ((ULONG_PTR) req->event_handle | 1);
+  }
+
+  success = handle->tcp.serv.func_acceptex(handle->socket,
+                                          accept_socket,
+                                          (void*)req->accept_buffer,
+                                          0,
+                                          sizeof(struct sockaddr_storage),
+                                          sizeof(struct sockaddr_storage),
+                                          &bytes,
+                                          &req->u.io.overlapped);
+
+  if (UV_SUCCEEDED_WITHOUT_IOCP(success)) {
+    /* Process the req without IOCP. */
+    req->accept_socket = accept_socket;
+    handle->reqs_pending++;
+    uv__insert_pending_req(loop, (uv_req_t*)req);
+  } else if (UV_SUCCEEDED_WITH_IOCP(success)) {
+    /* The req will be processed with IOCP. */
+    req->accept_socket = accept_socket;
+    handle->reqs_pending++;
+    if (handle->flags & UV_HANDLE_EMULATE_IOCP &&
+        req->wait_handle == INVALID_HANDLE_VALUE &&
+        !RegisterWaitForSingleObject(&req->wait_handle,
+          req->event_handle, post_completion, (void*) req,
+          INFINITE, WT_EXECUTEINWAITTHREAD)) {
+      SET_REQ_ERROR(req, GetLastError());
+      uv__insert_pending_req(loop, (uv_req_t*)req);
+    }
+  } else {
+    /* Make this req pending reporting an error. */
+    SET_REQ_ERROR(req, WSAGetLastError());
+    uv__insert_pending_req(loop, (uv_req_t*)req);
+    handle->reqs_pending++;
+    /* Destroy the preallocated client socket. */
+    closesocket(accept_socket);
+    /* Destroy the event handle */
+    if (handle->flags & UV_HANDLE_EMULATE_IOCP) {
+      CloseHandle(req->event_handle);
+      req->event_handle = NULL;
+    }
+  }
+}
+
+
+static void uv__tcp_queue_read(uv_loop_t* loop, uv_tcp_t* handle) {
+  uv_read_t* req;
+  uv_buf_t buf;
+  int result;
+  DWORD bytes, flags;
+
+  assert(handle->flags & UV_HANDLE_READING);
+  assert(!(handle->flags & UV_HANDLE_READ_PENDING));
+
+  req = &handle->read_req;
+  memset(&req->u.io.overlapped, 0, sizeof(req->u.io.overlapped));
+
+  /*
+   * Preallocate a read buffer if the number of active streams is below
+   * the threshold.
+  */
+  if (loop->active_tcp_streams < uv_active_tcp_streams_threshold) {
+    handle->flags &= ~UV_HANDLE_ZERO_READ;
+    handle->tcp.conn.read_buffer = uv_buf_init(NULL, 0);
+    handle->alloc_cb((uv_handle_t*) handle, 65536, &handle->tcp.conn.read_buffer);
+    if (handle->tcp.conn.read_buffer.base == NULL ||
+        handle->tcp.conn.read_buffer.len == 0) {
+      handle->read_cb((uv_stream_t*) handle, UV_ENOBUFS, &handle->tcp.conn.read_buffer);
+      return;
+    }
+    assert(handle->tcp.conn.read_buffer.base != NULL);
+    buf = handle->tcp.conn.read_buffer;
+  } else {
+    handle->flags |= UV_HANDLE_ZERO_READ;
+    buf.base = (char*) &uv_zero_;
+    buf.len = 0;
+  }
+
+  /* Prepare the overlapped structure. */
+  memset(&(req->u.io.overlapped), 0, sizeof(req->u.io.overlapped));
+  if (handle->flags & UV_HANDLE_EMULATE_IOCP) {
+    assert(req->event_handle != NULL);
+    req->u.io.overlapped.hEvent = (HANDLE) ((ULONG_PTR) req->event_handle | 1);
+  }
+
+  flags = 0;
+  result = WSARecv(handle->socket,
+                   (WSABUF*)&buf,
+                   1,
+                   &bytes,
+                   &flags,
+                   &req->u.io.overlapped,
+                   NULL);
+
+  handle->flags |= UV_HANDLE_READ_PENDING;
+  handle->reqs_pending++;
+
+  if (UV_SUCCEEDED_WITHOUT_IOCP(result == 0)) {
+    /* Process the req without IOCP. */
+    req->u.io.overlapped.InternalHigh = bytes;
+    uv__insert_pending_req(loop, (uv_req_t*)req);
+  } else if (UV_SUCCEEDED_WITH_IOCP(result == 0)) {
+    /* The req will be processed with IOCP. */
+    if (handle->flags & UV_HANDLE_EMULATE_IOCP &&
+        req->wait_handle == INVALID_HANDLE_VALUE &&
+        !RegisterWaitForSingleObject(&req->wait_handle,
+          req->event_handle, post_completion, (void*) req,
+          INFINITE, WT_EXECUTEINWAITTHREAD)) {
+      SET_REQ_ERROR(req, GetLastError());
+      uv__insert_pending_req(loop, (uv_req_t*)req);
+    }
+  } else {
+    /* Make this req pending reporting an error. */
+    SET_REQ_ERROR(req, WSAGetLastError());
+    uv__insert_pending_req(loop, (uv_req_t*)req);
+  }
+}
+
+
+int uv_tcp_close_reset(uv_tcp_t* handle, uv_close_cb close_cb) {
+  struct linger l = { 1, 0 };
+
+  /* Disallow setting SO_LINGER to zero due to some platform inconsistencies */
+  if (handle->flags & UV_HANDLE_SHUTTING)
+    return UV_EINVAL;
+
+  if (0 != setsockopt(handle->socket, SOL_SOCKET, SO_LINGER, (const char*)&l, sizeof(l)))
+    return uv_translate_sys_error(WSAGetLastError());
+
+  uv_close((uv_handle_t*) handle, close_cb);
+  return 0;
+}
+
+
+int uv__tcp_listen(uv_tcp_t* handle, int backlog, uv_connection_cb cb) {
+  unsigned int i, simultaneous_accepts;
+  uv_tcp_accept_t* req;
+  int err;
+
+  assert(backlog > 0);
+
+  if (handle->flags & UV_HANDLE_LISTENING) {
+    handle->stream.serv.connection_cb = cb;
+  }
+
+  if (handle->flags & UV_HANDLE_READING) {
+    return WSAEISCONN;
+  }
+
+  if (handle->delayed_error) {
+    return handle->delayed_error;
+  }
+
+  if (!(handle->flags & UV_HANDLE_BOUND)) {
+    err = uv__tcp_try_bind(handle,
+                           (const struct sockaddr*) &uv_addr_ip4_any_,
+                           sizeof(uv_addr_ip4_any_),
+                           0);
+    if (err)
+      return err;
+    if (handle->delayed_error)
+      return handle->delayed_error;
+  }
+
+  if (!handle->tcp.serv.func_acceptex) {
+    if (!uv__get_acceptex_function(handle->socket, &handle->tcp.serv.func_acceptex)) {
+      return WSAEAFNOSUPPORT;
+    }
+  }
+
+  /* If this flag is set, we already made this listen call in xfer. */
+  if (!(handle->flags & UV_HANDLE_SHARED_TCP_SOCKET) &&
+      listen(handle->socket, backlog) == SOCKET_ERROR) {
+    return WSAGetLastError();
+  }
+
+  handle->flags |= UV_HANDLE_LISTENING;
+  handle->stream.serv.connection_cb = cb;
+  INCREASE_ACTIVE_COUNT(loop, handle);
+
+  simultaneous_accepts = handle->flags & UV_HANDLE_TCP_SINGLE_ACCEPT ? 1
+    : uv_simultaneous_server_accepts;
+
+  if (handle->tcp.serv.accept_reqs == NULL) {
+    handle->tcp.serv.accept_reqs =
+      (uv_tcp_accept_t*)uv__malloc(uv_simultaneous_server_accepts * sizeof(uv_tcp_accept_t));
+    if (!handle->tcp.serv.accept_reqs) {
+      uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc");
+    }
+
+    for (i = 0; i < simultaneous_accepts; i++) {
+      req = &handle->tcp.serv.accept_reqs[i];
+      UV_REQ_INIT(req, UV_ACCEPT);
+      req->accept_socket = INVALID_SOCKET;
+      req->data = handle;
+
+      req->wait_handle = INVALID_HANDLE_VALUE;
+      if (handle->flags & UV_HANDLE_EMULATE_IOCP) {
+        req->event_handle = CreateEvent(NULL, 0, 0, NULL);
+        if (req->event_handle == NULL) {
+          uv_fatal_error(GetLastError(), "CreateEvent");
+        }
+      } else {
+        req->event_handle = NULL;
+      }
+
+      uv__tcp_queue_accept(handle, req);
+    }
+
+    /* Initialize other unused requests too, because uv_tcp_endgame doesn't
+     * know how many requests were initialized, so it will try to clean up
+     * {uv_simultaneous_server_accepts} requests. */
+    for (i = simultaneous_accepts; i < uv_simultaneous_server_accepts; i++) {
+      req = &handle->tcp.serv.accept_reqs[i];
+      UV_REQ_INIT(req, UV_ACCEPT);
+      req->accept_socket = INVALID_SOCKET;
+      req->data = handle;
+      req->wait_handle = INVALID_HANDLE_VALUE;
+      req->event_handle = NULL;
+    }
+  }
+
+  return 0;
+}
+
+
+int uv__tcp_accept(uv_tcp_t* server, uv_tcp_t* client) {
+  uv_loop_t* loop = server->loop;
+  int err = 0;
+  int family;
+
+  uv_tcp_accept_t* req = server->tcp.serv.pending_accepts;
+
+  if (!req) {
+    /* No valid connections found, so we error out. */
+    return WSAEWOULDBLOCK;
+  }
+
+  if (req->accept_socket == INVALID_SOCKET) {
+    return WSAENOTCONN;
+  }
+
+  if (server->flags & UV_HANDLE_IPV6) {
+    family = AF_INET6;
+  } else {
+    family = AF_INET;
+  }
+
+  err = uv__tcp_set_socket(client->loop,
+                          client,
+                          req->accept_socket,
+                          family,
+                          0);
+  if (err) {
+    closesocket(req->accept_socket);
+  } else {
+    uv__connection_init((uv_stream_t*) client);
+    /* AcceptEx() implicitly binds the accepted socket. */
+    client->flags |= UV_HANDLE_BOUND | UV_HANDLE_READABLE | UV_HANDLE_WRITABLE;
+  }
+
+  /* Prepare the req to pick up a new connection */
+  server->tcp.serv.pending_accepts = req->next_pending;
+  req->next_pending = NULL;
+  req->accept_socket = INVALID_SOCKET;
+
+  if (!(server->flags & UV_HANDLE_CLOSING)) {
+    /* Check if we're in a middle of changing the number of pending accepts. */
+    if (!(server->flags & UV_HANDLE_TCP_ACCEPT_STATE_CHANGING)) {
+      uv__tcp_queue_accept(server, req);
+    } else {
+      /* We better be switching to a single pending accept. */
+      assert(server->flags & UV_HANDLE_TCP_SINGLE_ACCEPT);
+
+      server->tcp.serv.processed_accepts++;
+
+      if (server->tcp.serv.processed_accepts >= uv_simultaneous_server_accepts) {
+        server->tcp.serv.processed_accepts = 0;
+        /*
+         * All previously queued accept requests are now processed.
+         * We now switch to queueing just a single accept.
+         */
+        uv__tcp_queue_accept(server, &server->tcp.serv.accept_reqs[0]);
+        server->flags &= ~UV_HANDLE_TCP_ACCEPT_STATE_CHANGING;
+        server->flags |= UV_HANDLE_TCP_SINGLE_ACCEPT;
+      }
+    }
+  }
+
+  loop->active_tcp_streams++;
+
+  return err;
+}
+
+
+int uv__tcp_read_start(uv_tcp_t* handle, uv_alloc_cb alloc_cb,
+    uv_read_cb read_cb) {
+  uv_loop_t* loop = handle->loop;
+
+  handle->flags |= UV_HANDLE_READING;
+  handle->read_cb = read_cb;
+  handle->alloc_cb = alloc_cb;
+  INCREASE_ACTIVE_COUNT(loop, handle);
+
+  /* If reading was stopped and then started again, there could still be a read
+   * request pending. */
+  if (!(handle->flags & UV_HANDLE_READ_PENDING)) {
+    if (handle->flags & UV_HANDLE_EMULATE_IOCP &&
+        handle->read_req.event_handle == NULL) {
+      handle->read_req.event_handle = CreateEvent(NULL, 0, 0, NULL);
+      if (handle->read_req.event_handle == NULL) {
+        uv_fatal_error(GetLastError(), "CreateEvent");
+      }
+    }
+    uv__tcp_queue_read(loop, handle);
+  }
+
+  return 0;
+}
+
+static int uv__is_loopback(const struct sockaddr_storage* storage) {
+  const struct sockaddr_in* in4;
+  const struct sockaddr_in6* in6;
+  int i;
+
+  if (storage->ss_family == AF_INET) {
+    in4 = (const struct sockaddr_in*) storage;
+    return in4->sin_addr.S_un.S_un_b.s_b1 == 127;
+  }
+  if (storage->ss_family == AF_INET6) {
+    in6 = (const struct sockaddr_in6*) storage;
+    for (i = 0; i < 7; ++i) {
+      if (in6->sin6_addr.u.Word[i] != 0)
+        return 0;
+    }
+    return in6->sin6_addr.u.Word[7] == htons(1);
+  }
+  return 0;
+}
+
+// Check if Windows version is 10.0.16299 or later
+static int uv__is_fast_loopback_fail_supported(void) {
+  OSVERSIONINFOW os_info;
+  if (!pRtlGetVersion)
+    return 0;
+  pRtlGetVersion(&os_info);
+  if (os_info.dwMajorVersion < 10)
+    return 0;
+  if (os_info.dwMajorVersion > 10)
+    return 1;
+  if (os_info.dwMinorVersion > 0)
+    return 1;
+  return os_info.dwBuildNumber >= 16299;
+}
+
+static int uv__tcp_try_connect(uv_connect_t* req,
+                              uv_tcp_t* handle,
+                              const struct sockaddr* addr,
+                              unsigned int addrlen,
+                              uv_connect_cb cb) {
+  uv_loop_t* loop = handle->loop;
+  TCP_INITIAL_RTO_PARAMETERS retransmit_ioctl;
+  const struct sockaddr* bind_addr;
+  struct sockaddr_storage converted;
+  BOOL success;
+  DWORD bytes;
+  int err;
+
+  err = uv__convert_to_localhost_if_unspecified(addr, &converted);
+  if (err)
+    return err;
+
+  if (handle->delayed_error != 0)
+    goto out;
+
+  if (!(handle->flags & UV_HANDLE_BOUND)) {
+    if (addrlen == sizeof(uv_addr_ip4_any_)) {
+      bind_addr = (const struct sockaddr*) &uv_addr_ip4_any_;
+    } else if (addrlen == sizeof(uv_addr_ip6_any_)) {
+      bind_addr = (const struct sockaddr*) &uv_addr_ip6_any_;
+    } else {
+      abort();
+    }
+    err = uv__tcp_try_bind(handle, bind_addr, addrlen, 0);
+    if (err)
+      return err;
+    if (handle->delayed_error != 0)
+      goto out;
+  }
+
+  if (!handle->tcp.conn.func_connectex) {
+    if (!uv__get_connectex_function(handle->socket, &handle->tcp.conn.func_connectex)) {
+      return WSAEAFNOSUPPORT;
+    }
+  }
+
+  /* This makes connect() fail instantly if the target port on the localhost
+   * is not reachable, instead of waiting for 2s. We do not care if this fails.
+   * This only works on Windows version 10.0.16299 and later.
+   */
+  if (uv__is_fast_loopback_fail_supported() && uv__is_loopback(&converted)) {
+    memset(&retransmit_ioctl, 0, sizeof(retransmit_ioctl));
+    retransmit_ioctl.Rtt = TCP_INITIAL_RTO_NO_SYN_RETRANSMISSIONS;
+    retransmit_ioctl.MaxSynRetransmissions = TCP_INITIAL_RTO_NO_SYN_RETRANSMISSIONS;
+    WSAIoctl(handle->socket,
+             SIO_TCP_INITIAL_RTO,
+             &retransmit_ioctl,
+             sizeof(retransmit_ioctl),
+             NULL,
+             0,
+             &bytes,
+             NULL,
+             NULL);
+  }
+
+out:
+
+  UV_REQ_INIT(req, UV_CONNECT);
+  req->handle = (uv_stream_t*) handle;
+  req->cb = cb;
+  memset(&req->u.io.overlapped, 0, sizeof(req->u.io.overlapped));
+
+  if (handle->delayed_error != 0) {
+    /* Process the req without IOCP. */
+    handle->reqs_pending++;
+    REGISTER_HANDLE_REQ(loop, handle, req);
+    uv__insert_pending_req(loop, (uv_req_t*)req);
+    return 0;
+  }
+
+  success = handle->tcp.conn.func_connectex(handle->socket,
+                                            (const struct sockaddr*) &converted,
+                                            addrlen,
+                                            NULL,
+                                            0,
+                                            &bytes,
+                                            &req->u.io.overlapped);
+
+  if (UV_SUCCEEDED_WITHOUT_IOCP(success)) {
+    /* Process the req without IOCP. */
+    handle->reqs_pending++;
+    REGISTER_HANDLE_REQ(loop, handle, req);
+    uv__insert_pending_req(loop, (uv_req_t*)req);
+  } else if (UV_SUCCEEDED_WITH_IOCP(success)) {
+    /* The req will be processed with IOCP. */
+    handle->reqs_pending++;
+    REGISTER_HANDLE_REQ(loop, handle, req);
+  } else {
+    return WSAGetLastError();
+  }
+
+  return 0;
+}
+
+
+int uv_tcp_getsockname(const uv_tcp_t* handle,
+                       struct sockaddr* name,
+                       int* namelen) {
+
+  return uv__getsockpeername((const uv_handle_t*) handle,
+                             getsockname,
+                             name,
+                             namelen,
+                             handle->delayed_error);
+}
+
+
+int uv_tcp_getpeername(const uv_tcp_t* handle,
+                       struct sockaddr* name,
+                       int* namelen) {
+
+  return uv__getsockpeername((const uv_handle_t*) handle,
+                             getpeername,
+                             name,
+                             namelen,
+                             handle->delayed_error);
+}
+
+
+int uv__tcp_write(uv_loop_t* loop,
+                 uv_write_t* req,
+                 uv_tcp_t* handle,
+                 const uv_buf_t bufs[],
+                 unsigned int nbufs,
+                 uv_write_cb cb) {
+  int result;
+  DWORD bytes;
+
+  UV_REQ_INIT(req, UV_WRITE);
+  req->handle = (uv_stream_t*) handle;
+  req->cb = cb;
+
+  /* Prepare the overlapped structure. */
+  memset(&(req->u.io.overlapped), 0, sizeof(req->u.io.overlapped));
+  if (handle->flags & UV_HANDLE_EMULATE_IOCP) {
+    req->event_handle = CreateEvent(NULL, 0, 0, NULL);
+    if (req->event_handle == NULL) {
+      uv_fatal_error(GetLastError(), "CreateEvent");
+    }
+    req->u.io.overlapped.hEvent = (HANDLE) ((ULONG_PTR) req->event_handle | 1);
+    req->wait_handle = INVALID_HANDLE_VALUE;
+  }
+
+  result = WSASend(handle->socket,
+                   (WSABUF*) bufs,
+                   nbufs,
+                   &bytes,
+                   0,
+                   &req->u.io.overlapped,
+                   NULL);
+
+  if (UV_SUCCEEDED_WITHOUT_IOCP(result == 0)) {
+    /* Request completed immediately. */
+    req->u.io.queued_bytes = 0;
+    handle->reqs_pending++;
+    handle->stream.conn.write_reqs_pending++;
+    REGISTER_HANDLE_REQ(loop, handle, req);
+    uv__insert_pending_req(loop, (uv_req_t*) req);
+  } else if (UV_SUCCEEDED_WITH_IOCP(result == 0)) {
+    /* Request queued by the kernel. */
+    req->u.io.queued_bytes = uv__count_bufs(bufs, nbufs);
+    handle->reqs_pending++;
+    handle->stream.conn.write_reqs_pending++;
+    REGISTER_HANDLE_REQ(loop, handle, req);
+    handle->write_queue_size += req->u.io.queued_bytes;
+    if (handle->flags & UV_HANDLE_EMULATE_IOCP &&
+        !RegisterWaitForSingleObject(&req->wait_handle,
+          req->event_handle, post_write_completion, (void*) req,
+          INFINITE, WT_EXECUTEINWAITTHREAD | WT_EXECUTEONLYONCE)) {
+      SET_REQ_ERROR(req, GetLastError());
+      uv__insert_pending_req(loop, (uv_req_t*)req);
+    }
+  } else {
+    /* Send failed due to an error, report it later */
+    req->u.io.queued_bytes = 0;
+    handle->reqs_pending++;
+    handle->stream.conn.write_reqs_pending++;
+    REGISTER_HANDLE_REQ(loop, handle, req);
+    SET_REQ_ERROR(req, WSAGetLastError());
+    uv__insert_pending_req(loop, (uv_req_t*) req);
+  }
+
+  return 0;
+}
+
+
+int uv__tcp_try_write(uv_tcp_t* handle,
+                     const uv_buf_t bufs[],
+                     unsigned int nbufs) {
+  int result;
+  DWORD bytes;
+
+  if (handle->stream.conn.write_reqs_pending > 0)
+    return UV_EAGAIN;
+
+  result = WSASend(handle->socket,
+                   (WSABUF*) bufs,
+                   nbufs,
+                   &bytes,
+                   0,
+                   NULL,
+                   NULL);
+
+  if (result == SOCKET_ERROR)
+    return uv_translate_sys_error(WSAGetLastError());
+  else
+    return bytes;
+}
+
+
+void uv__process_tcp_read_req(uv_loop_t* loop, uv_tcp_t* handle,
+    uv_req_t* req) {
+  DWORD bytes, flags, err;
+  uv_buf_t buf;
+  int count;
+
+  assert(handle->type == UV_TCP);
+
+  handle->flags &= ~UV_HANDLE_READ_PENDING;
+
+  if (!REQ_SUCCESS(req)) {
+    /* An error occurred doing the read. */
+    if ((handle->flags & UV_HANDLE_READING) ||
+        !(handle->flags & UV_HANDLE_ZERO_READ)) {
+      handle->flags &= ~UV_HANDLE_READING;
+      DECREASE_ACTIVE_COUNT(loop, handle);
+      buf = (handle->flags & UV_HANDLE_ZERO_READ) ?
+            uv_buf_init(NULL, 0) : handle->tcp.conn.read_buffer;
+
+      err = GET_REQ_SOCK_ERROR(req);
+
+      if (err == WSAECONNABORTED) {
+        /* Turn WSAECONNABORTED into UV_ECONNRESET to be consistent with Unix.
+         */
+        err = WSAECONNRESET;
+      }
+      handle->flags &= ~(UV_HANDLE_READABLE | UV_HANDLE_WRITABLE);
+
+      handle->read_cb((uv_stream_t*)handle,
+                      uv_translate_sys_error(err),
+                      &buf);
+    }
+  } else {
+    if (!(handle->flags & UV_HANDLE_ZERO_READ)) {
+      /* The read was done with a non-zero buffer length. */
+      if (req->u.io.overlapped.InternalHigh > 0) {
+        /* Successful read */
+        handle->read_cb((uv_stream_t*)handle,
+                        req->u.io.overlapped.InternalHigh,
+                        &handle->tcp.conn.read_buffer);
+        /* Read again only if bytes == buf.len */
+        if (req->u.io.overlapped.InternalHigh < handle->tcp.conn.read_buffer.len) {
+          goto done;
+        }
+      } else {
+        /* Connection closed */
+        if (handle->flags & UV_HANDLE_READING) {
+          handle->flags &= ~UV_HANDLE_READING;
+          DECREASE_ACTIVE_COUNT(loop, handle);
+        }
+
+        buf.base = 0;
+        buf.len = 0;
+        handle->read_cb((uv_stream_t*)handle, UV_EOF, &handle->tcp.conn.read_buffer);
+        goto done;
+      }
+    }
+
+    /* Do nonblocking reads until the buffer is empty */
+    count = 32;
+    while ((handle->flags & UV_HANDLE_READING) && (count-- > 0)) {
+      buf = uv_buf_init(NULL, 0);
+      handle->alloc_cb((uv_handle_t*) handle, 65536, &buf);
+      if (buf.base == NULL || buf.len == 0) {
+        handle->read_cb((uv_stream_t*) handle, UV_ENOBUFS, &buf);
+        break;
+      }
+      assert(buf.base != NULL);
+
+      flags = 0;
+      if (WSARecv(handle->socket,
+                  (WSABUF*)&buf,
+                  1,
+                  &bytes,
+                  &flags,
+                  NULL,
+                  NULL) != SOCKET_ERROR) {
+        if (bytes > 0) {
+          /* Successful read */
+          handle->read_cb((uv_stream_t*)handle, bytes, &buf);
+          /* Read again only if bytes == buf.len */
+          if (bytes < buf.len) {
+            break;
+          }
+        } else {
+          /* Connection closed */
+          handle->flags &= ~UV_HANDLE_READING;
+          DECREASE_ACTIVE_COUNT(loop, handle);
+
+          handle->read_cb((uv_stream_t*)handle, UV_EOF, &buf);
+          break;
+        }
+      } else {
+        err = WSAGetLastError();
+        if (err == WSAEWOULDBLOCK) {
+          /* Read buffer was completely empty, report a 0-byte read. */
+          handle->read_cb((uv_stream_t*)handle, 0, &buf);
+        } else {
+          /* Ouch! serious error. */
+          handle->flags &= ~UV_HANDLE_READING;
+          DECREASE_ACTIVE_COUNT(loop, handle);
+
+          if (err == WSAECONNABORTED) {
+            /* Turn WSAECONNABORTED into UV_ECONNRESET to be consistent with
+             * Unix. */
+            err = WSAECONNRESET;
+          }
+          handle->flags &= ~(UV_HANDLE_READABLE | UV_HANDLE_WRITABLE);
+
+          handle->read_cb((uv_stream_t*)handle,
+                          uv_translate_sys_error(err),
+                          &buf);
+        }
+        break;
+      }
+    }
+
+done:
+    /* Post another read if still reading and not closing. */
+    if ((handle->flags & UV_HANDLE_READING) &&
+        !(handle->flags & UV_HANDLE_READ_PENDING)) {
+      uv__tcp_queue_read(loop, handle);
+    }
+  }
+
+  DECREASE_PENDING_REQ_COUNT(handle);
+}
+
+
+void uv__process_tcp_write_req(uv_loop_t* loop, uv_tcp_t* handle,
+    uv_write_t* req) {
+  int err;
+
+  assert(handle->type == UV_TCP);
+
+  assert(handle->write_queue_size >= req->u.io.queued_bytes);
+  handle->write_queue_size -= req->u.io.queued_bytes;
+
+  UNREGISTER_HANDLE_REQ(loop, handle, req);
+
+  if (handle->flags & UV_HANDLE_EMULATE_IOCP) {
+    if (req->wait_handle != INVALID_HANDLE_VALUE) {
+      UnregisterWait(req->wait_handle);
+      req->wait_handle = INVALID_HANDLE_VALUE;
+    }
+    if (req->event_handle != NULL) {
+      CloseHandle(req->event_handle);
+      req->event_handle = NULL;
+    }
+  }
+
+  if (req->cb) {
+    err = uv_translate_sys_error(GET_REQ_SOCK_ERROR(req));
+    if (err == UV_ECONNABORTED) {
+      /* use UV_ECANCELED for consistency with Unix */
+      err = UV_ECANCELED;
+    }
+    req->cb(req, err);
+  }
+
+  handle->stream.conn.write_reqs_pending--;
+  if (handle->stream.conn.write_reqs_pending == 0) {
+    if (handle->flags & UV_HANDLE_CLOSING) {
+      closesocket(handle->socket);
+      handle->socket = INVALID_SOCKET;
+    }
+    if (handle->flags & UV_HANDLE_SHUTTING)
+      uv__process_tcp_shutdown_req(loop,
+                                   handle,
+                                   handle->stream.conn.shutdown_req);
+  }
+
+  DECREASE_PENDING_REQ_COUNT(handle);
+}
+
+
+void uv__process_tcp_accept_req(uv_loop_t* loop, uv_tcp_t* handle,
+    uv_req_t* raw_req) {
+  uv_tcp_accept_t* req = (uv_tcp_accept_t*) raw_req;
+  int err;
+
+  assert(handle->type == UV_TCP);
+
+  /* If handle->accepted_socket is not a valid socket, then uv_queue_accept
+   * must have failed. This is a serious error. We stop accepting connections
+   * and report this error to the connection callback. */
+  if (req->accept_socket == INVALID_SOCKET) {
+    if (handle->flags & UV_HANDLE_LISTENING) {
+      handle->flags &= ~UV_HANDLE_LISTENING;
+      DECREASE_ACTIVE_COUNT(loop, handle);
+      if (handle->stream.serv.connection_cb) {
+        err = GET_REQ_SOCK_ERROR(req);
+        handle->stream.serv.connection_cb((uv_stream_t*)handle,
+                                      uv_translate_sys_error(err));
+      }
+    }
+  } else if (REQ_SUCCESS(req) &&
+      setsockopt(req->accept_socket,
+                  SOL_SOCKET,
+                  SO_UPDATE_ACCEPT_CONTEXT,
+                  (char*)&handle->socket,
+                  sizeof(handle->socket)) == 0) {
+    req->next_pending = handle->tcp.serv.pending_accepts;
+    handle->tcp.serv.pending_accepts = req;
+
+    /* Accept and SO_UPDATE_ACCEPT_CONTEXT were successful. */
+    if (handle->stream.serv.connection_cb) {
+      handle->stream.serv.connection_cb((uv_stream_t*)handle, 0);
+    }
+  } else {
+    /* Error related to accepted socket is ignored because the server socket
+     * may still be healthy. If the server socket is broken uv_queue_accept
+     * will detect it. */
+    closesocket(req->accept_socket);
+    req->accept_socket = INVALID_SOCKET;
+    if (handle->flags & UV_HANDLE_LISTENING) {
+      uv__tcp_queue_accept(handle, req);
+    }
+  }
+
+  DECREASE_PENDING_REQ_COUNT(handle);
+}
+
+
+void uv__process_tcp_connect_req(uv_loop_t* loop, uv_tcp_t* handle,
+    uv_connect_t* req) {
+  int err;
+
+  assert(handle->type == UV_TCP);
+
+  UNREGISTER_HANDLE_REQ(loop, handle, req);
+
+  err = 0;
+  if (handle->delayed_error) {
+    /* To smooth over the differences between unixes errors that
+     * were reported synchronously on the first connect can be delayed
+     * until the next tick--which is now.
+     */
+    err = handle->delayed_error;
+    handle->delayed_error = 0;
+  } else if (REQ_SUCCESS(req)) {
+    if (handle->flags & UV_HANDLE_CLOSING) {
+      /* use UV_ECANCELED for consistency with Unix */
+      err = ERROR_OPERATION_ABORTED;
+    } else if (setsockopt(handle->socket,
+                          SOL_SOCKET,
+                          SO_UPDATE_CONNECT_CONTEXT,
+                          NULL,
+                          0) == 0) {
+      uv__connection_init((uv_stream_t*)handle);
+      handle->flags |= UV_HANDLE_READABLE | UV_HANDLE_WRITABLE;
+      loop->active_tcp_streams++;
+    } else {
+      err = WSAGetLastError();
+    }
+  } else {
+    err = GET_REQ_SOCK_ERROR(req);
+  }
+  req->cb(req, uv_translate_sys_error(err));
+
+  DECREASE_PENDING_REQ_COUNT(handle);
+}
+
+
+int uv__tcp_xfer_export(uv_tcp_t* handle,
+                        int target_pid,
+                        uv__ipc_socket_xfer_type_t* xfer_type,
+                        uv__ipc_socket_xfer_info_t* xfer_info) {
+  if (handle->flags & UV_HANDLE_CONNECTION) {
+    *xfer_type = UV__IPC_SOCKET_XFER_TCP_CONNECTION;
+  } else {
+    *xfer_type = UV__IPC_SOCKET_XFER_TCP_SERVER;
+    /* We're about to share the socket with another process. Because this is a
+     * listening socket, we assume that the other process will be accepting
+     * connections on it. Thus, before sharing the socket with another process,
+     * we call listen here in the parent process. */
+    if (!(handle->flags & UV_HANDLE_LISTENING)) {
+      if (!(handle->flags & UV_HANDLE_BOUND)) {
+        return ERROR_NOT_SUPPORTED;
+      }
+      if (handle->delayed_error == 0 &&
+          listen(handle->socket, SOMAXCONN) == SOCKET_ERROR) {
+        handle->delayed_error = WSAGetLastError();
+      }
+    }
+  }
+
+  if (WSADuplicateSocketW(handle->socket, target_pid, &xfer_info->socket_info))
+    return WSAGetLastError();
+  xfer_info->delayed_error = handle->delayed_error;
+
+  /* Mark the local copy of the handle as 'shared' so we behave in a way that's
+   * friendly to the process(es) that we share the socket with. */
+  handle->flags |= UV_HANDLE_SHARED_TCP_SOCKET;
+
+  return 0;
+}
+
+
+int uv__tcp_xfer_import(uv_tcp_t* tcp,
+                        uv__ipc_socket_xfer_type_t xfer_type,
+                        uv__ipc_socket_xfer_info_t* xfer_info) {
+  int err;
+  SOCKET socket;
+
+  assert(xfer_type == UV__IPC_SOCKET_XFER_TCP_SERVER ||
+         xfer_type == UV__IPC_SOCKET_XFER_TCP_CONNECTION);
+
+  socket = WSASocketW(FROM_PROTOCOL_INFO,
+                      FROM_PROTOCOL_INFO,
+                      FROM_PROTOCOL_INFO,
+                      &xfer_info->socket_info,
+                      0,
+                      WSA_FLAG_OVERLAPPED);
+
+  if (socket == INVALID_SOCKET) {
+    return WSAGetLastError();
+  }
+
+  err = uv__tcp_set_socket(
+      tcp->loop, tcp, socket, xfer_info->socket_info.iAddressFamily, 1);
+  if (err) {
+    closesocket(socket);
+    return err;
+  }
+
+  tcp->delayed_error = xfer_info->delayed_error;
+  tcp->flags |= UV_HANDLE_BOUND | UV_HANDLE_SHARED_TCP_SOCKET;
+
+  if (xfer_type == UV__IPC_SOCKET_XFER_TCP_CONNECTION) {
+    uv__connection_init((uv_stream_t*)tcp);
+    tcp->flags |= UV_HANDLE_READABLE | UV_HANDLE_WRITABLE;
+  }
+
+  tcp->loop->active_tcp_streams++;
+  return 0;
+}
+
+
+int uv_tcp_nodelay(uv_tcp_t* handle, int enable) {
+  int err;
+
+  if (handle->socket != INVALID_SOCKET) {
+    err = uv__tcp_nodelay(handle, handle->socket, enable);
+    if (err)
+      return uv_translate_sys_error(err);
+  }
+
+  if (enable) {
+    handle->flags |= UV_HANDLE_TCP_NODELAY;
+  } else {
+    handle->flags &= ~UV_HANDLE_TCP_NODELAY;
+  }
+
+  return 0;
+}
+
+
+int uv_tcp_keepalive(uv_tcp_t* handle, int enable, unsigned int delay) {
+  int err;
+
+  if (handle->socket != INVALID_SOCKET) {
+    err = uv__tcp_keepalive(handle, handle->socket, enable, delay);
+    if (err)
+      return uv_translate_sys_error(err);
+  }
+
+  if (enable) {
+    handle->flags |= UV_HANDLE_TCP_KEEPALIVE;
+  } else {
+    handle->flags &= ~UV_HANDLE_TCP_KEEPALIVE;
+  }
+
+  /* TODO: Store delay if handle->socket isn't created yet. */
+
+  return 0;
+}
+
+
+int uv_tcp_simultaneous_accepts(uv_tcp_t* handle, int enable) {
+  if (handle->flags & UV_HANDLE_CONNECTION) {
+    return UV_EINVAL;
+  }
+
+  /* Check if we're already in the desired mode. */
+  if ((enable && !(handle->flags & UV_HANDLE_TCP_SINGLE_ACCEPT)) ||
+      (!enable && handle->flags & UV_HANDLE_TCP_SINGLE_ACCEPT)) {
+    return 0;
+  }
+
+  /* Don't allow switching from single pending accept to many. */
+  if (enable) {
+    return UV_ENOTSUP;
+  }
+
+  /* Check if we're in a middle of changing the number of pending accepts. */
+  if (handle->flags & UV_HANDLE_TCP_ACCEPT_STATE_CHANGING) {
+    return 0;
+  }
+
+  handle->flags |= UV_HANDLE_TCP_SINGLE_ACCEPT;
+
+  /* Flip the changing flag if we have already queued multiple accepts. */
+  if (handle->flags & UV_HANDLE_LISTENING) {
+    handle->flags |= UV_HANDLE_TCP_ACCEPT_STATE_CHANGING;
+  }
+
+  return 0;
+}
+
+
+static void uv__tcp_try_cancel_reqs(uv_tcp_t* tcp) {
+  SOCKET socket;
+  int non_ifs_lsp;
+  int reading;
+  int writing;
+
+  socket = tcp->socket;
+  reading = tcp->flags & UV_HANDLE_READ_PENDING;
+  writing = tcp->stream.conn.write_reqs_pending > 0;
+  if (!reading && !writing)
+    return;
+
+  /* TODO: in libuv v2, keep explicit track of write_reqs, so we can cancel
+   * them each explicitly with CancelIoEx (like unix). */
+  if (reading)
+    CancelIoEx((HANDLE) socket, &tcp->read_req.u.io.overlapped);
+  if (writing)
+    CancelIo((HANDLE) socket);
+
+  /* Check if we have any non-IFS LSPs stacked on top of TCP */
+  non_ifs_lsp = (tcp->flags & UV_HANDLE_IPV6) ? uv_tcp_non_ifs_lsp_ipv6 :
+                                                uv_tcp_non_ifs_lsp_ipv4;
+
+  /* If there are non-ifs LSPs then try to obtain a base handle for the socket.
+   * This will always fail on Windows XP/3k. */
+  if (non_ifs_lsp) {
+    DWORD bytes;
+    if (WSAIoctl(socket,
+                 SIO_BASE_HANDLE,
+                 NULL,
+                 0,
+                 &socket,
+                 sizeof socket,
+                 &bytes,
+                 NULL,
+                 NULL) != 0) {
+      /* Failed. We can't do CancelIo. */
+      return;
+    }
+  }
+
+  assert(socket != 0 && socket != INVALID_SOCKET);
+
+  if (socket != tcp->socket) {
+    if (reading)
+      CancelIoEx((HANDLE) socket, &tcp->read_req.u.io.overlapped);
+    if (writing)
+      CancelIo((HANDLE) socket);
+  }
+}
+
+
+void uv__tcp_close(uv_loop_t* loop, uv_tcp_t* tcp) {
+  if (tcp->flags & UV_HANDLE_CONNECTION) {
+    if (tcp->flags & UV_HANDLE_READING) {
+      uv_read_stop((uv_stream_t*) tcp);
+    }
+    uv__tcp_try_cancel_reqs(tcp);
+  } else {
+    if (tcp->tcp.serv.accept_reqs != NULL) {
+      /* First close the incoming sockets to cancel the accept operations before
+       * we free their resources. */
+      unsigned int i;
+      for (i = 0; i < uv_simultaneous_server_accepts; i++) {
+        uv_tcp_accept_t* req = &tcp->tcp.serv.accept_reqs[i];
+        if (req->accept_socket != INVALID_SOCKET) {
+          closesocket(req->accept_socket);
+          req->accept_socket = INVALID_SOCKET;
+        }
+      }
+    }
+    assert(!(tcp->flags & UV_HANDLE_READING));
+  }
+
+  if (tcp->flags & UV_HANDLE_LISTENING) {
+    tcp->flags &= ~UV_HANDLE_LISTENING;
+    DECREASE_ACTIVE_COUNT(loop, tcp);
+  }
+
+  tcp->flags &= ~(UV_HANDLE_READABLE | UV_HANDLE_WRITABLE);
+  uv__handle_closing(tcp);
+
+  /* If any overlapped req failed to cancel, calling `closesocket` now would
+   * cause Win32 to send an RST packet. Try to avoid that for writes, if
+   * possibly applicable, by waiting to process the completion notifications
+   * first (which typically should be cancellations). There's not much we can
+   * do about canceled reads, which also will generate an RST packet. */
+  if (!(tcp->flags & UV_HANDLE_CONNECTION) ||
+      tcp->stream.conn.write_reqs_pending == 0) {
+    closesocket(tcp->socket);
+    tcp->socket = INVALID_SOCKET;
+  }
+
+  if (tcp->reqs_pending == 0)
+    uv__want_endgame(loop, (uv_handle_t*) tcp);
+}
+
+
+int uv_tcp_open(uv_tcp_t* handle, uv_os_sock_t sock) {
+  WSAPROTOCOL_INFOW protocol_info;
+  int opt_len;
+  int err;
+  struct sockaddr_storage saddr;
+  int saddr_len;
+
+  /* Detect the address family of the socket. */
+  opt_len = (int) sizeof protocol_info;
+  if (getsockopt(sock,
+                 SOL_SOCKET,
+                 SO_PROTOCOL_INFOW,
+                 (char*) &protocol_info,
+                 &opt_len) == SOCKET_ERROR) {
+    return uv_translate_sys_error(GetLastError());
+  }
+
+  err = uv__tcp_set_socket(handle->loop,
+                          handle,
+                          sock,
+                          protocol_info.iAddressFamily,
+                          1);
+  if (err) {
+    return uv_translate_sys_error(err);
+  }
+
+  /* Support already active socket. */
+  saddr_len = sizeof(saddr);
+  if (!uv_tcp_getsockname(handle, (struct sockaddr*) &saddr, &saddr_len)) {
+    /* Socket is already bound. */
+    handle->flags |= UV_HANDLE_BOUND;
+    saddr_len = sizeof(saddr);
+    if (!uv_tcp_getpeername(handle, (struct sockaddr*) &saddr, &saddr_len)) {
+      /* Socket is already connected. */
+      uv__connection_init((uv_stream_t*) handle);
+      handle->flags |= UV_HANDLE_READABLE | UV_HANDLE_WRITABLE;
+    }
+  }
+
+  return 0;
+}
+
+
+/* This function is an egress point, i.e. it returns libuv errors rather than
+ * system errors.
+ */
+int uv__tcp_bind(uv_tcp_t* handle,
+                 const struct sockaddr* addr,
+                 unsigned int addrlen,
+                 unsigned int flags) {
+  int err;
+
+  err = uv__tcp_try_bind(handle, addr, addrlen, flags);
+  if (err)
+    return uv_translate_sys_error(err);
+
+  return 0;
+}
+
+
+/* This function is an egress point, i.e. it returns libuv errors rather than
+ * system errors.
+ */
+int uv__tcp_connect(uv_connect_t* req,
+                    uv_tcp_t* handle,
+                    const struct sockaddr* addr,
+                    unsigned int addrlen,
+                    uv_connect_cb cb) {
+  int err;
+
+  err = uv__tcp_try_connect(req, handle, addr, addrlen, cb);
+  if (err)
+    return uv_translate_sys_error(err);
+
+  return 0;
+}
+
+#ifndef WSA_FLAG_NO_HANDLE_INHERIT
+/* Added in Windows 7 SP1. Specify this to avoid race conditions, */
+/* but also manually clear the inherit flag in case this failed. */
+#define WSA_FLAG_NO_HANDLE_INHERIT 0x80
+#endif
+
+int uv_socketpair(int type, int protocol, uv_os_sock_t fds[2], int flags0, int flags1) {
+  SOCKET server = INVALID_SOCKET;
+  SOCKET client0 = INVALID_SOCKET;
+  SOCKET client1 = INVALID_SOCKET;
+  SOCKADDR_IN name;
+  LPFN_ACCEPTEX func_acceptex;
+  WSAOVERLAPPED overlap;
+  char accept_buffer[sizeof(struct sockaddr_storage) * 2 + 32];
+  int namelen;
+  int err;
+  DWORD bytes;
+  DWORD flags;
+  DWORD client0_flags = WSA_FLAG_NO_HANDLE_INHERIT;
+  DWORD client1_flags = WSA_FLAG_NO_HANDLE_INHERIT;
+
+  if (flags0 & UV_NONBLOCK_PIPE)
+      client0_flags |= WSA_FLAG_OVERLAPPED;
+  if (flags1 & UV_NONBLOCK_PIPE)
+      client1_flags |= WSA_FLAG_OVERLAPPED;
+
+  server = WSASocketW(AF_INET, type, protocol, NULL, 0,
+                      WSA_FLAG_OVERLAPPED | WSA_FLAG_NO_HANDLE_INHERIT);
+  if (server == INVALID_SOCKET)
+    goto wsaerror;
+  if (!SetHandleInformation((HANDLE) server, HANDLE_FLAG_INHERIT, 0))
+    goto error;
+  name.sin_family = AF_INET;
+  name.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+  name.sin_port = 0;
+  if (bind(server, (SOCKADDR*) &name, sizeof(name)) != 0)
+    goto wsaerror;
+  if (listen(server, 1) != 0)
+    goto wsaerror;
+  namelen = sizeof(name);
+  if (getsockname(server, (SOCKADDR*) &name, &namelen) != 0)
+    goto wsaerror;
+  client0 = WSASocketW(AF_INET, type, protocol, NULL, 0, client0_flags);
+  if (client0 == INVALID_SOCKET)
+    goto wsaerror;
+  if (!SetHandleInformation((HANDLE) client0, HANDLE_FLAG_INHERIT, 0))
+    goto error;
+  if (connect(client0, (SOCKADDR*) &name, sizeof(name)) != 0)
+    goto wsaerror;
+  client1 = WSASocketW(AF_INET, type, protocol, NULL, 0, client1_flags);
+  if (client1 == INVALID_SOCKET)
+    goto wsaerror;
+  if (!SetHandleInformation((HANDLE) client1, HANDLE_FLAG_INHERIT, 0))
+    goto error;
+  if (!uv__get_acceptex_function(server, &func_acceptex)) {
+    err = WSAEAFNOSUPPORT;
+    goto cleanup;
+  }
+  memset(&overlap, 0, sizeof(overlap));
+  if (!func_acceptex(server,
+                     client1,
+                     accept_buffer,
+                     0,
+                     sizeof(struct sockaddr_storage),
+                     sizeof(struct sockaddr_storage),
+                     &bytes,
+                     &overlap)) {
+    err = WSAGetLastError();
+    if (err == ERROR_IO_PENDING) {
+      /* Result should complete immediately, since we already called connect,
+       * but empirically, we sometimes have to poll the kernel a couple times
+       * until it notices that. */
+      while (!WSAGetOverlappedResult(client1, &overlap, &bytes, FALSE, &flags)) {
+        err = WSAGetLastError();
+        if (err != WSA_IO_INCOMPLETE)
+          goto cleanup;
+        SwitchToThread();
+      }
+    }
+    else {
+      goto cleanup;
+    }
+  }
+  if (setsockopt(client1, SOL_SOCKET, SO_UPDATE_ACCEPT_CONTEXT,
+                  (char*) &server, sizeof(server)) != 0) {
+    goto wsaerror;
+  }
+
+  closesocket(server);
+
+  fds[0] = client0;
+  fds[1] = client1;
+
+  return 0;
+
+ wsaerror:
+    err = WSAGetLastError();
+    goto cleanup;
+
+ error:
+    err = GetLastError();
+    goto cleanup;
+
+ cleanup:
+    if (server != INVALID_SOCKET)
+      closesocket(server);
+    if (client0 != INVALID_SOCKET)
+      closesocket(client0);
+    if (client1 != INVALID_SOCKET)
+      closesocket(client1);
+
+    assert(err);
+    return uv_translate_sys_error(err);
+}
diff --git a/wpinet/src/main/native/thirdparty/libuv/src/win/thread.cpp b/wpinet/src/main/native/thirdparty/libuv/src/win/thread.cpp
new file mode 100644
index 0000000..9ad60c9
--- /dev/null
+++ b/wpinet/src/main/native/thirdparty/libuv/src/win/thread.cpp
@@ -0,0 +1,479 @@
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include <assert.h>
+#include <limits.h>
+#include <stdlib.h>
+
+#if defined(__MINGW64_VERSION_MAJOR)
+/* MemoryBarrier expands to __mm_mfence in some cases (x86+sse2), which may
+ * require this header in some versions of mingw64. */
+#include <intrin.h>
+#endif
+
+#include "uv.h"
+#include "internal.h"
+
+static void uv__once_inner(uv_once_t* guard, void (*callback)(void)) {
+  DWORD result;
+  HANDLE existing_event, created_event;
+
+  created_event = CreateEvent(NULL, 1, 0, NULL);
+  if (created_event == 0) {
+    /* Could fail in a low-memory situation? */
+    uv_fatal_error(GetLastError(), "CreateEvent");
+  }
+
+  existing_event = InterlockedCompareExchangePointer(&guard->event,
+                                                     created_event,
+                                                     NULL);
+
+  if (existing_event == NULL) {
+    /* We won the race */
+    callback();
+
+    result = SetEvent(created_event);
+    assert(result);
+    guard->ran = 1;
+
+  } else {
+    /* We lost the race. Destroy the event we created and wait for the existing
+     * one to become signaled. */
+    CloseHandle(created_event);
+    result = WaitForSingleObject(existing_event, INFINITE);
+    assert(result == WAIT_OBJECT_0);
+  }
+}
+
+
+void uv_once(uv_once_t* guard, void (*callback)(void)) {
+  /* Fast case - avoid WaitForSingleObject. */
+  if (guard->ran) {
+    return;
+  }
+
+  uv__once_inner(guard, callback);
+}
+
+
+/* Verify that uv_thread_t can be stored in a TLS slot. */
+STATIC_ASSERT(sizeof(uv_thread_t) <= sizeof(void*));
+
+static uv_key_t uv__current_thread_key;
+static uv_once_t uv__current_thread_init_guard = UV_ONCE_INIT;
+
+
+static void uv__init_current_thread_key(void) {
+  if (uv_key_create(&uv__current_thread_key))
+    abort();
+}
+
+
+struct thread_ctx {
+  void (*entry)(void* arg);
+  void* arg;
+  uv_thread_t self;
+};
+
+
+static UINT __stdcall uv__thread_start(void* arg) {
+  struct thread_ctx *ctx_p;
+  struct thread_ctx ctx;
+
+  ctx_p = (struct thread_ctx*)arg;
+  ctx = *ctx_p;
+  uv__free(ctx_p);
+
+  uv_once(&uv__current_thread_init_guard, uv__init_current_thread_key);
+  uv_key_set(&uv__current_thread_key, ctx.self);
+
+  ctx.entry(ctx.arg);
+
+  return 0;
+}
+
+
+int uv_thread_create(uv_thread_t *tid, void (*entry)(void *arg), void *arg) {
+  uv_thread_options_t params;
+  params.flags = UV_THREAD_NO_FLAGS;
+  return uv_thread_create_ex(tid, &params, entry, arg);
+}
+
+int uv_thread_create_ex(uv_thread_t* tid,
+                        const uv_thread_options_t* params,
+                        void (*entry)(void *arg),
+                        void *arg) {
+  struct thread_ctx* ctx;
+  int err;
+  HANDLE thread;
+  SYSTEM_INFO sysinfo;
+  size_t stack_size;
+  size_t pagesize;
+
+  stack_size =
+      params->flags & UV_THREAD_HAS_STACK_SIZE ? params->stack_size : 0;
+
+  if (stack_size != 0) {
+    GetNativeSystemInfo(&sysinfo);
+    pagesize = (size_t)sysinfo.dwPageSize;
+    /* Round up to the nearest page boundary. */
+    stack_size = (stack_size + pagesize - 1) &~ (pagesize - 1);
+
+    if ((unsigned)stack_size != stack_size)
+      return UV_EINVAL;
+  }
+
+  ctx = (struct thread_ctx*)uv__malloc(sizeof(*ctx));
+  if (ctx == NULL)
+    return UV_ENOMEM;
+
+  ctx->entry = entry;
+  ctx->arg = arg;
+
+  /* Create the thread in suspended state so we have a chance to pass
+   * its own creation handle to it */
+  thread = (HANDLE) _beginthreadex(NULL,
+                                   (unsigned)stack_size,
+                                   uv__thread_start,
+                                   ctx,
+                                   CREATE_SUSPENDED,
+                                   NULL);
+  if (thread == NULL) {
+    err = errno;
+    uv__free(ctx);
+  } else {
+    err = 0;
+    *tid = thread;
+    ctx->self = thread;
+    ResumeThread(thread);
+  }
+
+  switch (err) {
+    case 0:
+      return 0;
+    case EACCES:
+      return UV_EACCES;
+    case EAGAIN:
+      return UV_EAGAIN;
+    case EINVAL:
+      return UV_EINVAL;
+  }
+
+  return UV_EIO;
+}
+
+
+uv_thread_t uv_thread_self(void) {
+  uv_thread_t key;
+  uv_once(&uv__current_thread_init_guard, uv__init_current_thread_key);
+  key = uv_key_get(&uv__current_thread_key);
+  if (key == NULL) {
+      /* If the thread wasn't started by uv_thread_create (such as the main
+       * thread), we assign an id to it now. */
+      if (!DuplicateHandle(GetCurrentProcess(), GetCurrentThread(),
+                           GetCurrentProcess(), &key, 0,
+                           FALSE, DUPLICATE_SAME_ACCESS)) {
+          uv_fatal_error(GetLastError(), "DuplicateHandle");
+      }
+      uv_key_set(&uv__current_thread_key, key);
+  }
+  return key;
+}
+
+
+int uv_thread_join(uv_thread_t *tid) {
+  if (WaitForSingleObject(*tid, INFINITE))
+    return uv_translate_sys_error(GetLastError());
+  else {
+    CloseHandle(*tid);
+    *tid = 0;
+    MemoryBarrier();  /* For feature parity with pthread_join(). */
+    return 0;
+  }
+}
+
+
+int uv_thread_equal(const uv_thread_t* t1, const uv_thread_t* t2) {
+  return *t1 == *t2;
+}
+
+
+int uv_mutex_init(uv_mutex_t* mutex) {
+  InitializeCriticalSection(mutex);
+  return 0;
+}
+
+
+int uv_mutex_init_recursive(uv_mutex_t* mutex) {
+  return uv_mutex_init(mutex);
+}
+
+
+void uv_mutex_destroy(uv_mutex_t* mutex) {
+  DeleteCriticalSection(mutex);
+}
+
+
+void uv_mutex_lock(uv_mutex_t* mutex) {
+  EnterCriticalSection(mutex);
+}
+
+
+int uv_mutex_trylock(uv_mutex_t* mutex) {
+  if (TryEnterCriticalSection(mutex))
+    return 0;
+  else
+    return UV_EBUSY;
+}
+
+
+void uv_mutex_unlock(uv_mutex_t* mutex) {
+  LeaveCriticalSection(mutex);
+}
+
+/* Ensure that the ABI for this type remains stable in v1.x */
+#ifdef _WIN64
+STATIC_ASSERT(sizeof(uv_rwlock_t) == 80);
+#else
+STATIC_ASSERT(sizeof(uv_rwlock_t) == 48);
+#endif
+
+int uv_rwlock_init(uv_rwlock_t* rwlock) {
+  memset(rwlock, 0, sizeof(*rwlock));
+  InitializeSRWLock(&rwlock->read_write_lock_);
+
+  return 0;
+}
+
+
+void uv_rwlock_destroy(uv_rwlock_t* rwlock) {
+  /* SRWLock does not need explicit destruction so long as there are no waiting threads
+     See: https://docs.microsoft.com/windows/win32/api/synchapi/nf-synchapi-initializesrwlock#remarks */
+}
+
+
+void uv_rwlock_rdlock(uv_rwlock_t* rwlock) {
+  AcquireSRWLockShared(&rwlock->read_write_lock_);
+}
+
+
+int uv_rwlock_tryrdlock(uv_rwlock_t* rwlock) {
+  if (!TryAcquireSRWLockShared(&rwlock->read_write_lock_))
+    return UV_EBUSY;
+
+  return 0;
+}
+
+
+void uv_rwlock_rdunlock(uv_rwlock_t* rwlock) {
+  ReleaseSRWLockShared(&rwlock->read_write_lock_);
+}
+
+
+void uv_rwlock_wrlock(uv_rwlock_t* rwlock) {
+  AcquireSRWLockExclusive(&rwlock->read_write_lock_);
+}
+
+
+int uv_rwlock_trywrlock(uv_rwlock_t* rwlock) {
+  if (!TryAcquireSRWLockExclusive(&rwlock->read_write_lock_))
+    return UV_EBUSY;
+
+  return 0;
+}
+
+
+void uv_rwlock_wrunlock(uv_rwlock_t* rwlock) {
+  ReleaseSRWLockExclusive(&rwlock->read_write_lock_);
+}
+
+
+int uv_sem_init(uv_sem_t* sem, unsigned int value) {
+  *sem = CreateSemaphore(NULL, value, INT_MAX, NULL);
+  if (*sem == NULL)
+    return uv_translate_sys_error(GetLastError());
+  else
+    return 0;
+}
+
+
+void uv_sem_destroy(uv_sem_t* sem) {
+  if (!CloseHandle(*sem))
+    abort();
+}
+
+
+void uv_sem_post(uv_sem_t* sem) {
+  if (!ReleaseSemaphore(*sem, 1, NULL))
+    abort();
+}
+
+
+void uv_sem_wait(uv_sem_t* sem) {
+  if (WaitForSingleObject(*sem, INFINITE) != WAIT_OBJECT_0)
+    abort();
+}
+
+
+int uv_sem_trywait(uv_sem_t* sem) {
+  DWORD r = WaitForSingleObject(*sem, 0);
+
+  if (r == WAIT_OBJECT_0)
+    return 0;
+
+  if (r == WAIT_TIMEOUT)
+    return UV_EAGAIN;
+
+  abort();
+  return -1; /* Satisfy the compiler. */
+}
+
+
+int uv_cond_init(uv_cond_t* cond) {
+  InitializeConditionVariable(&cond->cond_var);
+  return 0;
+}
+
+
+void uv_cond_destroy(uv_cond_t* cond) {
+  /* nothing to do */
+  (void) &cond;
+}
+
+
+void uv_cond_signal(uv_cond_t* cond) {
+  WakeConditionVariable(&cond->cond_var);
+}
+
+
+void uv_cond_broadcast(uv_cond_t* cond) {
+  WakeAllConditionVariable(&cond->cond_var);
+}
+
+
+void uv_cond_wait(uv_cond_t* cond, uv_mutex_t* mutex) {
+  if (!SleepConditionVariableCS(&cond->cond_var, mutex, INFINITE))
+    abort();
+}
+
+int uv_cond_timedwait(uv_cond_t* cond, uv_mutex_t* mutex, uint64_t timeout) {
+  if (SleepConditionVariableCS(&cond->cond_var, mutex, (DWORD)(timeout / 1e6)))
+    return 0;
+  if (GetLastError() != ERROR_TIMEOUT)
+    abort();
+  return UV_ETIMEDOUT;
+}
+
+
+int uv_barrier_init(uv_barrier_t* barrier, unsigned int count) {
+  int err;
+
+  barrier->n = count;
+  barrier->count = 0;
+
+  err = uv_mutex_init(&barrier->mutex);
+  if (err)
+    return err;
+
+  err = uv_sem_init(&barrier->turnstile1, 0);
+  if (err)
+    goto error2;
+
+  err = uv_sem_init(&barrier->turnstile2, 1);
+  if (err)
+    goto error;
+
+  return 0;
+
+error:
+  uv_sem_destroy(&barrier->turnstile1);
+error2:
+  uv_mutex_destroy(&barrier->mutex);
+  return err;
+
+}
+
+
+void uv_barrier_destroy(uv_barrier_t* barrier) {
+  uv_sem_destroy(&barrier->turnstile2);
+  uv_sem_destroy(&barrier->turnstile1);
+  uv_mutex_destroy(&barrier->mutex);
+}
+
+
+int uv_barrier_wait(uv_barrier_t* barrier) {
+  int serial_thread;
+
+  uv_mutex_lock(&barrier->mutex);
+  if (++barrier->count == barrier->n) {
+    uv_sem_wait(&barrier->turnstile2);
+    uv_sem_post(&barrier->turnstile1);
+  }
+  uv_mutex_unlock(&barrier->mutex);
+
+  uv_sem_wait(&barrier->turnstile1);
+  uv_sem_post(&barrier->turnstile1);
+
+  uv_mutex_lock(&barrier->mutex);
+  serial_thread = (--barrier->count == 0);
+  if (serial_thread) {
+    uv_sem_wait(&barrier->turnstile1);
+    uv_sem_post(&barrier->turnstile2);
+  }
+  uv_mutex_unlock(&barrier->mutex);
+
+  uv_sem_wait(&barrier->turnstile2);
+  uv_sem_post(&barrier->turnstile2);
+  return serial_thread;
+}
+
+
+int uv_key_create(uv_key_t* key) {
+  key->tls_index = TlsAlloc();
+  if (key->tls_index == TLS_OUT_OF_INDEXES)
+    return UV_ENOMEM;
+  return 0;
+}
+
+
+void uv_key_delete(uv_key_t* key) {
+  if (TlsFree(key->tls_index) == FALSE)
+    abort();
+  key->tls_index = TLS_OUT_OF_INDEXES;
+}
+
+
+void* uv_key_get(uv_key_t* key) {
+  void* value;
+
+  value = TlsGetValue(key->tls_index);
+  if (value == NULL)
+    if (GetLastError() != ERROR_SUCCESS)
+      abort();
+
+  return value;
+}
+
+
+void uv_key_set(uv_key_t* key, void* value) {
+  if (TlsSetValue(key->tls_index, value) == FALSE)
+    abort();
+}
diff --git a/wpinet/src/main/native/thirdparty/libuv/src/win/tty.cpp b/wpinet/src/main/native/thirdparty/libuv/src/win/tty.cpp
new file mode 100644
index 0000000..9753784
--- /dev/null
+++ b/wpinet/src/main/native/thirdparty/libuv/src/win/tty.cpp
@@ -0,0 +1,2459 @@
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#define _CRT_NONSTDC_NO_WARNINGS
+
+#include <assert.h>
+#include <io.h>
+#include <string.h>
+#include <stdlib.h>
+
+#if defined(_MSC_VER) && _MSC_VER < 1600
+# include "uv/stdint-msvc2008.h"
+#else
+# include <stdint.h>
+#endif
+
+#ifndef COMMON_LVB_REVERSE_VIDEO
+# define COMMON_LVB_REVERSE_VIDEO 0x4000
+#endif
+
+#include "uv.h"
+#include "internal.h"
+#include "handle-inl.h"
+#include "stream-inl.h"
+#include "req-inl.h"
+
+#pragma comment(lib, "User32.lib")
+
+#ifndef InterlockedOr
+# define InterlockedOr _InterlockedOr
+#endif
+
+#define UNICODE_REPLACEMENT_CHARACTER (0xfffd)
+
+#define ANSI_NORMAL           0x0000
+#define ANSI_ESCAPE_SEEN      0x0002
+#define ANSI_CSI              0x0004
+#define ANSI_ST_CONTROL       0x0008
+#define ANSI_IGNORE           0x0010
+#define ANSI_IN_ARG           0x0020
+#define ANSI_IN_STRING        0x0040
+#define ANSI_BACKSLASH_SEEN   0x0080
+#define ANSI_EXTENSION        0x0100
+#define ANSI_DECSCUSR         0x0200
+
+#define MAX_INPUT_BUFFER_LENGTH 8192
+#define MAX_CONSOLE_CHAR 8192
+
+#ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING
+#define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004
+#endif
+
+#define CURSOR_SIZE_SMALL     25
+#define CURSOR_SIZE_LARGE     100
+
+static void uv__tty_capture_initial_style(
+    CONSOLE_SCREEN_BUFFER_INFO* screen_buffer_info,
+    CONSOLE_CURSOR_INFO* cursor_info);
+static void uv__tty_update_virtual_window(CONSOLE_SCREEN_BUFFER_INFO* info);
+static int uv__cancel_read_console(uv_tty_t* handle);
+
+
+/* Null uv_buf_t */
+static const uv_buf_t uv_null_buf_ = { 0, NULL };
+
+enum uv__read_console_status_e {
+  NOT_STARTED,
+  IN_PROGRESS,
+  TRAP_REQUESTED,
+  COMPLETED
+};
+
+static volatile LONG uv__read_console_status = NOT_STARTED;
+static volatile LONG uv__restore_screen_state;
+static CONSOLE_SCREEN_BUFFER_INFO uv__saved_screen_state;
+
+
+/*
+ * The console virtual window.
+ *
+ * Normally cursor movement in windows is relative to the console screen buffer,
+ * e.g. the application is allowed to overwrite the 'history'. This is very
+ * inconvenient, it makes absolute cursor movement pretty useless. There is
+ * also the concept of 'client rect' which is defined by the actual size of
+ * the console window and the scroll position of the screen buffer, but it's
+ * very volatile because it changes when the user scrolls.
+ *
+ * To make cursor movement behave sensibly we define a virtual window to which
+ * cursor movement is confined. The virtual window is always as wide as the
+ * console screen buffer, but it's height is defined by the size of the
+ * console window. The top of the virtual window aligns with the position
+ * of the caret when the first stdout/err handle is created, unless that would
+ * mean that it would extend beyond the bottom of the screen buffer -  in that
+ * that case it's located as far down as possible.
+ *
+ * When the user writes a long text or many newlines, such that the output
+ * reaches beyond the bottom of the virtual window, the virtual window is
+ * shifted downwards, but not resized.
+ *
+ * Since all tty i/o happens on the same console, this window is shared
+ * between all stdout/stderr handles.
+ */
+
+static int uv_tty_virtual_offset = -1;
+static int uv_tty_virtual_height = -1;
+static int uv_tty_virtual_width = -1;
+
+/* The console window size
+ * We keep this separate from uv_tty_virtual_*. We use those values to only
+ * handle signalling SIGWINCH
+ */
+
+static HANDLE uv__tty_console_handle = INVALID_HANDLE_VALUE;
+static int uv__tty_console_height = -1;
+static int uv__tty_console_width = -1;
+static HANDLE uv__tty_console_resized = INVALID_HANDLE_VALUE;
+static uv_mutex_t uv__tty_console_resize_mutex;
+
+static DWORD WINAPI uv__tty_console_resize_message_loop_thread(void* param);
+static void CALLBACK uv__tty_console_resize_event(HWINEVENTHOOK hWinEventHook,
+                                                  DWORD event,
+                                                  HWND hwnd,
+                                                  LONG idObject,
+                                                  LONG idChild,
+                                                  DWORD dwEventThread,
+                                                  DWORD dwmsEventTime);
+static DWORD WINAPI uv__tty_console_resize_watcher_thread(void* param);
+static void uv__tty_console_signal_resize(void);
+
+/* We use a semaphore rather than a mutex or critical section because in some
+   cases (uv__cancel_read_console) we need take the lock in the main thread and
+   release it in another thread. Using a semaphore ensures that in such
+   scenario the main thread will still block when trying to acquire the lock. */
+static uv_sem_t uv_tty_output_lock;
+
+static WORD uv_tty_default_text_attributes =
+    FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;
+
+static char uv_tty_default_fg_color = 7;
+static char uv_tty_default_bg_color = 0;
+static char uv_tty_default_fg_bright = 0;
+static char uv_tty_default_bg_bright = 0;
+static char uv_tty_default_inverse = 0;
+
+static CONSOLE_CURSOR_INFO uv_tty_default_cursor_info;
+
+/* Determine whether or not ANSI support is enabled. */
+static BOOL uv__need_check_vterm_state = TRUE;
+static uv_tty_vtermstate_t uv__vterm_state = UV_TTY_UNSUPPORTED;
+static void uv__determine_vterm_state(HANDLE handle);
+
+void uv__console_init(void) {
+  if (uv_sem_init(&uv_tty_output_lock, 1))
+    abort();
+  uv__tty_console_handle = CreateFileW(L"CONOUT$",
+                                       GENERIC_READ | GENERIC_WRITE,
+                                       FILE_SHARE_WRITE,
+                                       0,
+                                       OPEN_EXISTING,
+                                       0,
+                                       0);
+  if (uv__tty_console_handle != INVALID_HANDLE_VALUE) {
+    CONSOLE_SCREEN_BUFFER_INFO sb_info;
+    QueueUserWorkItem(uv__tty_console_resize_message_loop_thread,
+                      NULL,
+                      WT_EXECUTELONGFUNCTION);
+    uv_mutex_init(&uv__tty_console_resize_mutex);
+    if (GetConsoleScreenBufferInfo(uv__tty_console_handle, &sb_info)) {
+      uv__tty_console_width = sb_info.dwSize.X;
+      uv__tty_console_height = sb_info.srWindow.Bottom - sb_info.srWindow.Top + 1;
+    }
+  }
+}
+
+
+int uv_tty_init(uv_loop_t* loop, uv_tty_t* tty, uv_file fd, int unused) {
+  BOOL readable;
+  DWORD NumberOfEvents;
+  HANDLE handle;
+  CONSOLE_SCREEN_BUFFER_INFO screen_buffer_info;
+  CONSOLE_CURSOR_INFO cursor_info;
+  (void)unused;
+
+  uv__once_init();
+  handle = (HANDLE) uv__get_osfhandle(fd);
+  if (handle == INVALID_HANDLE_VALUE)
+    return UV_EBADF;
+
+  if (fd <= 2) {
+    /* In order to avoid closing a stdio file descriptor 0-2, duplicate the
+     * underlying OS handle and forget about the original fd.
+     * We could also opt to use the original OS handle and just never close it,
+     * but then there would be no reliable way to cancel pending read operations
+     * upon close.
+     */
+    if (!DuplicateHandle(INVALID_HANDLE_VALUE,
+                         handle,
+                         INVALID_HANDLE_VALUE,
+                         &handle,
+                         0,
+                         FALSE,
+                         DUPLICATE_SAME_ACCESS))
+      return uv_translate_sys_error(GetLastError());
+    fd = -1;
+  }
+
+  readable = GetNumberOfConsoleInputEvents(handle, &NumberOfEvents);
+  if (!readable) {
+    /* Obtain the screen buffer info with the output handle. */
+    if (!GetConsoleScreenBufferInfo(handle, &screen_buffer_info)) {
+      return uv_translate_sys_error(GetLastError());
+    }
+
+    /* Obtain the cursor info with the output handle. */
+    if (!GetConsoleCursorInfo(handle, &cursor_info)) {
+      return uv_translate_sys_error(GetLastError());
+    }
+
+    /* Obtain the tty_output_lock because the virtual window state is shared
+     * between all uv_tty_t handles. */
+    uv_sem_wait(&uv_tty_output_lock);
+
+    if (uv__need_check_vterm_state)
+      uv__determine_vterm_state(handle);
+
+    /* Remember the original console text attributes and cursor info. */
+    uv__tty_capture_initial_style(&screen_buffer_info, &cursor_info);
+
+    uv__tty_update_virtual_window(&screen_buffer_info);
+
+    uv_sem_post(&uv_tty_output_lock);
+  }
+
+
+  uv__stream_init(loop, (uv_stream_t*) tty, UV_TTY);
+  uv__connection_init((uv_stream_t*) tty);
+
+  tty->handle = handle;
+  tty->u.fd = fd;
+  tty->reqs_pending = 0;
+  tty->flags |= UV_HANDLE_BOUND;
+
+  if (readable) {
+    /* Initialize TTY input specific fields. */
+    tty->flags |= UV_HANDLE_TTY_READABLE | UV_HANDLE_READABLE;
+    /* TODO: remove me in v2.x. */
+    tty->tty.rd.unused_ = NULL;
+    tty->tty.rd.read_line_buffer = uv_null_buf_;
+    tty->tty.rd.read_raw_wait = NULL;
+
+    /* Init keycode-to-vt100 mapper state. */
+    tty->tty.rd.last_key_len = 0;
+    tty->tty.rd.last_key_offset = 0;
+    tty->tty.rd.last_utf16_high_surrogate = 0;
+    memset(&tty->tty.rd.last_input_record, 0, sizeof tty->tty.rd.last_input_record);
+  } else {
+    /* TTY output specific fields. */
+    tty->flags |= UV_HANDLE_WRITABLE;
+
+    /* Init utf8-to-utf16 conversion state. */
+    tty->tty.wr.utf8_bytes_left = 0;
+    tty->tty.wr.utf8_codepoint = 0;
+
+    /* Initialize eol conversion state */
+    tty->tty.wr.previous_eol = 0;
+
+    /* Init ANSI parser state. */
+    tty->tty.wr.ansi_parser_state = ANSI_NORMAL;
+  }
+
+  return 0;
+}
+
+
+/* Set the default console text attributes based on how the console was
+ * configured when libuv started.
+ */
+static void uv__tty_capture_initial_style(
+    CONSOLE_SCREEN_BUFFER_INFO* screen_buffer_info,
+    CONSOLE_CURSOR_INFO* cursor_info) {
+  static int style_captured = 0;
+
+  /* Only do this once.
+     Assumption: Caller has acquired uv_tty_output_lock. */
+  if (style_captured)
+    return;
+
+  /* Save raw win32 attributes. */
+  uv_tty_default_text_attributes = screen_buffer_info->wAttributes;
+
+  /* Convert black text on black background to use white text. */
+  if (uv_tty_default_text_attributes == 0)
+    uv_tty_default_text_attributes = 7;
+
+  /* Convert Win32 attributes to ANSI colors. */
+  uv_tty_default_fg_color = 0;
+  uv_tty_default_bg_color = 0;
+  uv_tty_default_fg_bright = 0;
+  uv_tty_default_bg_bright = 0;
+  uv_tty_default_inverse = 0;
+
+  if (uv_tty_default_text_attributes & FOREGROUND_RED)
+    uv_tty_default_fg_color |= 1;
+
+  if (uv_tty_default_text_attributes & FOREGROUND_GREEN)
+    uv_tty_default_fg_color |= 2;
+
+  if (uv_tty_default_text_attributes & FOREGROUND_BLUE)
+    uv_tty_default_fg_color |= 4;
+
+  if (uv_tty_default_text_attributes & BACKGROUND_RED)
+    uv_tty_default_bg_color |= 1;
+
+  if (uv_tty_default_text_attributes & BACKGROUND_GREEN)
+    uv_tty_default_bg_color |= 2;
+
+  if (uv_tty_default_text_attributes & BACKGROUND_BLUE)
+    uv_tty_default_bg_color |= 4;
+
+  if (uv_tty_default_text_attributes & FOREGROUND_INTENSITY)
+    uv_tty_default_fg_bright = 1;
+
+  if (uv_tty_default_text_attributes & BACKGROUND_INTENSITY)
+    uv_tty_default_bg_bright = 1;
+
+  if (uv_tty_default_text_attributes & COMMON_LVB_REVERSE_VIDEO)
+    uv_tty_default_inverse = 1;
+
+  /* Save the cursor size and the cursor state. */
+  uv_tty_default_cursor_info = *cursor_info;
+
+  style_captured = 1;
+}
+
+
+int uv_tty_set_mode(uv_tty_t* tty, uv_tty_mode_t mode) {
+  DWORD flags;
+  unsigned char was_reading;
+  uv_alloc_cb alloc_cb;
+  uv_read_cb read_cb;
+  int err;
+
+  if (!(tty->flags & UV_HANDLE_TTY_READABLE)) {
+    return UV_EINVAL;
+  }
+
+  if (!!mode == !!(tty->flags & UV_HANDLE_TTY_RAW)) {
+    return 0;
+  }
+
+  switch (mode) {
+    case UV_TTY_MODE_NORMAL:
+      flags = ENABLE_ECHO_INPUT | ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT;
+      break;
+    case UV_TTY_MODE_RAW:
+      flags = ENABLE_WINDOW_INPUT;
+      break;
+    case UV_TTY_MODE_IO:
+      return UV_ENOTSUP;
+    default:
+      return UV_EINVAL;
+  }
+
+  /* If currently reading, stop, and restart reading. */
+  if (tty->flags & UV_HANDLE_READING) {
+    was_reading = 1;
+    alloc_cb = tty->alloc_cb;
+    read_cb = tty->read_cb;
+    err = uv__tty_read_stop(tty);
+    if (err) {
+      return uv_translate_sys_error(err);
+    }
+  } else {
+    was_reading = 0;
+    alloc_cb = NULL;
+    read_cb = NULL;
+  }
+
+  uv_sem_wait(&uv_tty_output_lock);
+  if (!SetConsoleMode(tty->handle, flags)) {
+    err = uv_translate_sys_error(GetLastError());
+    uv_sem_post(&uv_tty_output_lock);
+    return err;
+  }
+  uv_sem_post(&uv_tty_output_lock);
+
+  /* Update flag. */
+  tty->flags &= ~UV_HANDLE_TTY_RAW;
+  tty->flags |= mode ? UV_HANDLE_TTY_RAW : 0;
+
+  /* If we just stopped reading, restart. */
+  if (was_reading) {
+    err = uv__tty_read_start(tty, alloc_cb, read_cb);
+    if (err) {
+      return uv_translate_sys_error(err);
+    }
+  }
+
+  return 0;
+}
+
+
+int uv_tty_get_winsize(uv_tty_t* tty, int* width, int* height) {
+  CONSOLE_SCREEN_BUFFER_INFO info;
+
+  if (!GetConsoleScreenBufferInfo(tty->handle, &info)) {
+    return uv_translate_sys_error(GetLastError());
+  }
+
+  uv_sem_wait(&uv_tty_output_lock);
+  uv__tty_update_virtual_window(&info);
+  uv_sem_post(&uv_tty_output_lock);
+
+  *width = uv_tty_virtual_width;
+  *height = uv_tty_virtual_height;
+
+  return 0;
+}
+
+
+static void CALLBACK uv_tty_post_raw_read(void* data, BOOLEAN didTimeout) {
+  uv_loop_t* loop;
+  uv_tty_t* handle;
+  uv_req_t* req;
+
+  assert(data);
+  assert(!didTimeout);
+
+  req = (uv_req_t*) data;
+  handle = (uv_tty_t*) req->data;
+  loop = handle->loop;
+
+  UnregisterWait(handle->tty.rd.read_raw_wait);
+  handle->tty.rd.read_raw_wait = NULL;
+
+  SET_REQ_SUCCESS(req);
+  POST_COMPLETION_FOR_REQ(loop, req);
+}
+
+
+static void uv__tty_queue_read_raw(uv_loop_t* loop, uv_tty_t* handle) {
+  uv_read_t* req;
+  BOOL r;
+
+  assert(handle->flags & UV_HANDLE_READING);
+  assert(!(handle->flags & UV_HANDLE_READ_PENDING));
+
+  assert(handle->handle && handle->handle != INVALID_HANDLE_VALUE);
+
+  handle->tty.rd.read_line_buffer = uv_null_buf_;
+
+  req = &handle->read_req;
+  memset(&req->u.io.overlapped, 0, sizeof(req->u.io.overlapped));
+
+  r = RegisterWaitForSingleObject(&handle->tty.rd.read_raw_wait,
+                                  handle->handle,
+                                  uv_tty_post_raw_read,
+                                  (void*) req,
+                                  INFINITE,
+                                  WT_EXECUTEINWAITTHREAD | WT_EXECUTEONLYONCE);
+  if (!r) {
+    handle->tty.rd.read_raw_wait = NULL;
+    SET_REQ_ERROR(req, GetLastError());
+    uv__insert_pending_req(loop, (uv_req_t*)req);
+  }
+
+  handle->flags |= UV_HANDLE_READ_PENDING;
+  handle->reqs_pending++;
+}
+
+
+static DWORD CALLBACK uv_tty_line_read_thread(void* data) {
+  uv_loop_t* loop;
+  uv_tty_t* handle;
+  uv_req_t* req;
+  DWORD bytes, read_bytes;
+  WCHAR utf16[MAX_INPUT_BUFFER_LENGTH / 3];
+  DWORD chars, read_chars;
+  LONG status;
+  COORD pos;
+  BOOL read_console_success;
+
+  assert(data);
+
+  req = (uv_req_t*) data;
+  handle = (uv_tty_t*) req->data;
+  loop = handle->loop;
+
+  assert(handle->tty.rd.read_line_buffer.base != NULL);
+  assert(handle->tty.rd.read_line_buffer.len > 0);
+
+  /* ReadConsole can't handle big buffers. */
+  if (handle->tty.rd.read_line_buffer.len < MAX_INPUT_BUFFER_LENGTH) {
+    bytes = handle->tty.rd.read_line_buffer.len;
+  } else {
+    bytes = MAX_INPUT_BUFFER_LENGTH;
+  }
+
+  /* At last, unicode! One utf-16 codeunit never takes more than 3 utf-8
+   * codeunits to encode. */
+  chars = bytes / 3;
+
+  status = InterlockedExchange(&uv__read_console_status, IN_PROGRESS);
+  if (status == TRAP_REQUESTED) {
+    SET_REQ_SUCCESS(req);
+    InterlockedExchange(&uv__read_console_status, COMPLETED);
+    req->u.io.overlapped.InternalHigh = 0;
+    POST_COMPLETION_FOR_REQ(loop, req);
+    return 0;
+  }
+
+  read_console_success = ReadConsoleW(handle->handle,
+                                      (void*) utf16,
+                                      chars,
+                                      &read_chars,
+                                      NULL);
+
+  if (read_console_success) {
+    read_bytes = WideCharToMultiByte(CP_UTF8,
+                                     0,
+                                     utf16,
+                                     read_chars,
+                                     handle->tty.rd.read_line_buffer.base,
+                                     bytes,
+                                     NULL,
+                                     NULL);
+    SET_REQ_SUCCESS(req);
+    req->u.io.overlapped.InternalHigh = read_bytes;
+  } else {
+    SET_REQ_ERROR(req, GetLastError());
+  }
+
+  status = InterlockedExchange(&uv__read_console_status, COMPLETED);
+
+  if (status ==  TRAP_REQUESTED) {
+    /* If we canceled the read by sending a VK_RETURN event, restore the
+       screen state to undo the visual effect of the VK_RETURN */
+    if (read_console_success && InterlockedOr(&uv__restore_screen_state, 0)) {
+      HANDLE active_screen_buffer;
+      active_screen_buffer = CreateFileA("conout$",
+                                         GENERIC_READ | GENERIC_WRITE,
+                                         FILE_SHARE_READ | FILE_SHARE_WRITE,
+                                         NULL,
+                                         OPEN_EXISTING,
+                                         FILE_ATTRIBUTE_NORMAL,
+                                         NULL);
+      if (active_screen_buffer != INVALID_HANDLE_VALUE) {
+        pos = uv__saved_screen_state.dwCursorPosition;
+
+        /* If the cursor was at the bottom line of the screen buffer, the
+           VK_RETURN would have caused the buffer contents to scroll up by one
+           line. The right position to reset the cursor to is therefore one line
+           higher */
+        if (pos.Y == uv__saved_screen_state.dwSize.Y - 1)
+          pos.Y--;
+
+        SetConsoleCursorPosition(active_screen_buffer, pos);
+        CloseHandle(active_screen_buffer);
+      }
+    }
+    uv_sem_post(&uv_tty_output_lock);
+  }
+  POST_COMPLETION_FOR_REQ(loop, req);
+  return 0;
+}
+
+
+static void uv__tty_queue_read_line(uv_loop_t* loop, uv_tty_t* handle) {
+  uv_read_t* req;
+  BOOL r;
+
+  assert(handle->flags & UV_HANDLE_READING);
+  assert(!(handle->flags & UV_HANDLE_READ_PENDING));
+  assert(handle->handle && handle->handle != INVALID_HANDLE_VALUE);
+
+  req = &handle->read_req;
+  memset(&req->u.io.overlapped, 0, sizeof(req->u.io.overlapped));
+
+  handle->tty.rd.read_line_buffer = uv_buf_init(NULL, 0);
+  handle->alloc_cb((uv_handle_t*) handle, 8192, &handle->tty.rd.read_line_buffer);
+  if (handle->tty.rd.read_line_buffer.base == NULL ||
+      handle->tty.rd.read_line_buffer.len == 0) {
+    handle->read_cb((uv_stream_t*) handle,
+                    UV_ENOBUFS,
+                    &handle->tty.rd.read_line_buffer);
+    return;
+  }
+  assert(handle->tty.rd.read_line_buffer.base != NULL);
+
+  /* Reset flags  No locking is required since there cannot be a line read
+     in progress. We are also relying on the memory barrier provided by
+     QueueUserWorkItem*/
+  uv__restore_screen_state = FALSE;
+  uv__read_console_status = NOT_STARTED;
+  r = QueueUserWorkItem(uv_tty_line_read_thread,
+                        (void*) req,
+                        WT_EXECUTELONGFUNCTION);
+  if (!r) {
+    SET_REQ_ERROR(req, GetLastError());
+    uv__insert_pending_req(loop, (uv_req_t*)req);
+  }
+
+  handle->flags |= UV_HANDLE_READ_PENDING;
+  handle->reqs_pending++;
+}
+
+
+static void uv__tty_queue_read(uv_loop_t* loop, uv_tty_t* handle) {
+  if (handle->flags & UV_HANDLE_TTY_RAW) {
+    uv__tty_queue_read_raw(loop, handle);
+  } else {
+    uv__tty_queue_read_line(loop, handle);
+  }
+}
+
+
+static const char* get_vt100_fn_key(DWORD code, char shift, char ctrl,
+    size_t* len) {
+#define VK_CASE(vk, normal_str, shift_str, ctrl_str, shift_ctrl_str)          \
+    case (vk):                                                                \
+      if (shift && ctrl) {                                                    \
+        *len = sizeof shift_ctrl_str;                                         \
+        return "\033" shift_ctrl_str;                                         \
+      } else if (shift) {                                                     \
+        *len = sizeof shift_str ;                                             \
+        return "\033" shift_str;                                              \
+      } else if (ctrl) {                                                      \
+        *len = sizeof ctrl_str;                                               \
+        return "\033" ctrl_str;                                               \
+      } else {                                                                \
+        *len = sizeof normal_str;                                             \
+        return "\033" normal_str;                                             \
+      }
+
+  switch (code) {
+    /* These mappings are the same as Cygwin's. Unmodified and alt-modified
+     * keypad keys comply with linux console, modifiers comply with xterm
+     * modifier usage. F1. f12 and shift-f1. f10 comply with linux console, f6.
+     * f12 with and without modifiers comply with rxvt. */
+    VK_CASE(VK_INSERT,  "[2~",  "[2;2~", "[2;5~", "[2;6~")
+    VK_CASE(VK_END,     "[4~",  "[4;2~", "[4;5~", "[4;6~")
+    VK_CASE(VK_DOWN,    "[B",   "[1;2B", "[1;5B", "[1;6B")
+    VK_CASE(VK_NEXT,    "[6~",  "[6;2~", "[6;5~", "[6;6~")
+    VK_CASE(VK_LEFT,    "[D",   "[1;2D", "[1;5D", "[1;6D")
+    VK_CASE(VK_CLEAR,   "[G",   "[1;2G", "[1;5G", "[1;6G")
+    VK_CASE(VK_RIGHT,   "[C",   "[1;2C", "[1;5C", "[1;6C")
+    VK_CASE(VK_UP,      "[A",   "[1;2A", "[1;5A", "[1;6A")
+    VK_CASE(VK_HOME,    "[1~",  "[1;2~", "[1;5~", "[1;6~")
+    VK_CASE(VK_PRIOR,   "[5~",  "[5;2~", "[5;5~", "[5;6~")
+    VK_CASE(VK_DELETE,  "[3~",  "[3;2~", "[3;5~", "[3;6~")
+    VK_CASE(VK_NUMPAD0, "[2~",  "[2;2~", "[2;5~", "[2;6~")
+    VK_CASE(VK_NUMPAD1, "[4~",  "[4;2~", "[4;5~", "[4;6~")
+    VK_CASE(VK_NUMPAD2, "[B",   "[1;2B", "[1;5B", "[1;6B")
+    VK_CASE(VK_NUMPAD3, "[6~",  "[6;2~", "[6;5~", "[6;6~")
+    VK_CASE(VK_NUMPAD4, "[D",   "[1;2D", "[1;5D", "[1;6D")
+    VK_CASE(VK_NUMPAD5, "[G",   "[1;2G", "[1;5G", "[1;6G")
+    VK_CASE(VK_NUMPAD6, "[C",   "[1;2C", "[1;5C", "[1;6C")
+    VK_CASE(VK_NUMPAD7, "[A",   "[1;2A", "[1;5A", "[1;6A")
+    VK_CASE(VK_NUMPAD8, "[1~",  "[1;2~", "[1;5~", "[1;6~")
+    VK_CASE(VK_NUMPAD9, "[5~",  "[5;2~", "[5;5~", "[5;6~")
+    VK_CASE(VK_DECIMAL, "[3~",  "[3;2~", "[3;5~", "[3;6~")
+    VK_CASE(VK_F1,      "[[A",  "[23~",  "[11^",  "[23^" )
+    VK_CASE(VK_F2,      "[[B",  "[24~",  "[12^",  "[24^" )
+    VK_CASE(VK_F3,      "[[C",  "[25~",  "[13^",  "[25^" )
+    VK_CASE(VK_F4,      "[[D",  "[26~",  "[14^",  "[26^" )
+    VK_CASE(VK_F5,      "[[E",  "[28~",  "[15^",  "[28^" )
+    VK_CASE(VK_F6,      "[17~", "[29~",  "[17^",  "[29^" )
+    VK_CASE(VK_F7,      "[18~", "[31~",  "[18^",  "[31^" )
+    VK_CASE(VK_F8,      "[19~", "[32~",  "[19^",  "[32^" )
+    VK_CASE(VK_F9,      "[20~", "[33~",  "[20^",  "[33^" )
+    VK_CASE(VK_F10,     "[21~", "[34~",  "[21^",  "[34^" )
+    VK_CASE(VK_F11,     "[23~", "[23$",  "[23^",  "[23@" )
+    VK_CASE(VK_F12,     "[24~", "[24$",  "[24^",  "[24@" )
+
+    default:
+      *len = 0;
+      return NULL;
+  }
+#undef VK_CASE
+}
+
+
+void uv_process_tty_read_raw_req(uv_loop_t* loop, uv_tty_t* handle,
+    uv_req_t* req) {
+  /* Shortcut for handle->tty.rd.last_input_record.Event.KeyEvent. */
+#define KEV handle->tty.rd.last_input_record.Event.KeyEvent
+
+  DWORD records_left, records_read;
+  uv_buf_t buf;
+  off_t buf_used;
+
+  assert(handle->type == UV_TTY);
+  assert(handle->flags & UV_HANDLE_TTY_READABLE);
+  handle->flags &= ~UV_HANDLE_READ_PENDING;
+
+  if (!(handle->flags & UV_HANDLE_READING) ||
+      !(handle->flags & UV_HANDLE_TTY_RAW)) {
+    goto out;
+  }
+
+  if (!REQ_SUCCESS(req)) {
+    /* An error occurred while waiting for the event. */
+    if ((handle->flags & UV_HANDLE_READING)) {
+      handle->flags &= ~UV_HANDLE_READING;
+      handle->read_cb((uv_stream_t*)handle,
+                      uv_translate_sys_error(GET_REQ_ERROR(req)),
+                      &uv_null_buf_);
+    }
+    goto out;
+  }
+
+  /* Fetch the number of events  */
+  if (!GetNumberOfConsoleInputEvents(handle->handle, &records_left)) {
+    handle->flags &= ~UV_HANDLE_READING;
+    DECREASE_ACTIVE_COUNT(loop, handle);
+    handle->read_cb((uv_stream_t*)handle,
+                    uv_translate_sys_error(GetLastError()),
+                    &uv_null_buf_);
+    goto out;
+  }
+
+  /* Windows sends a lot of events that we're not interested in, so buf will be
+   * allocated on demand, when there's actually something to emit. */
+  buf = uv_null_buf_;
+  buf_used = 0;
+
+  while ((records_left > 0 || handle->tty.rd.last_key_len > 0) &&
+         (handle->flags & UV_HANDLE_READING)) {
+    if (handle->tty.rd.last_key_len == 0) {
+      /* Read the next input record */
+      if (!ReadConsoleInputW(handle->handle,
+                             &handle->tty.rd.last_input_record,
+                             1,
+                             &records_read)) {
+        handle->flags &= ~UV_HANDLE_READING;
+        DECREASE_ACTIVE_COUNT(loop, handle);
+        handle->read_cb((uv_stream_t*) handle,
+                        uv_translate_sys_error(GetLastError()),
+                        &buf);
+        goto out;
+      }
+      records_left--;
+
+      /* We might be not subscribed to EVENT_CONSOLE_LAYOUT or we might be
+       * running under some TTY emulator that does not send those events. */
+      if (handle->tty.rd.last_input_record.EventType == WINDOW_BUFFER_SIZE_EVENT) {
+        uv__tty_console_signal_resize();
+      }
+
+      /* Ignore other events that are not key events. */
+      if (handle->tty.rd.last_input_record.EventType != KEY_EVENT) {
+        continue;
+      }
+
+      /* Ignore keyup events, unless the left alt key was held and a valid
+       * unicode character was emitted. */
+      if (!KEV.bKeyDown &&
+          (KEV.wVirtualKeyCode != VK_MENU ||
+           KEV.uChar.UnicodeChar == 0)) {
+        continue;
+      }
+
+      /* Ignore keypresses to numpad number keys if the left alt is held
+       * because the user is composing a character, or windows simulating this.
+       */
+      if ((KEV.dwControlKeyState & LEFT_ALT_PRESSED) &&
+          !(KEV.dwControlKeyState & ENHANCED_KEY) &&
+          (KEV.wVirtualKeyCode == VK_INSERT ||
+          KEV.wVirtualKeyCode == VK_END ||
+          KEV.wVirtualKeyCode == VK_DOWN ||
+          KEV.wVirtualKeyCode == VK_NEXT ||
+          KEV.wVirtualKeyCode == VK_LEFT ||
+          KEV.wVirtualKeyCode == VK_CLEAR ||
+          KEV.wVirtualKeyCode == VK_RIGHT ||
+          KEV.wVirtualKeyCode == VK_HOME ||
+          KEV.wVirtualKeyCode == VK_UP ||
+          KEV.wVirtualKeyCode == VK_PRIOR ||
+          KEV.wVirtualKeyCode == VK_NUMPAD0 ||
+          KEV.wVirtualKeyCode == VK_NUMPAD1 ||
+          KEV.wVirtualKeyCode == VK_NUMPAD2 ||
+          KEV.wVirtualKeyCode == VK_NUMPAD3 ||
+          KEV.wVirtualKeyCode == VK_NUMPAD4 ||
+          KEV.wVirtualKeyCode == VK_NUMPAD5 ||
+          KEV.wVirtualKeyCode == VK_NUMPAD6 ||
+          KEV.wVirtualKeyCode == VK_NUMPAD7 ||
+          KEV.wVirtualKeyCode == VK_NUMPAD8 ||
+          KEV.wVirtualKeyCode == VK_NUMPAD9)) {
+        continue;
+      }
+
+      if (KEV.uChar.UnicodeChar != 0) {
+        int prefix_len, char_len;
+
+        /* Character key pressed */
+        if (KEV.uChar.UnicodeChar >= 0xD800 &&
+            KEV.uChar.UnicodeChar < 0xDC00) {
+          /* UTF-16 high surrogate */
+          handle->tty.rd.last_utf16_high_surrogate = KEV.uChar.UnicodeChar;
+          continue;
+        }
+
+        /* Prefix with \u033 if alt was held, but alt was not used as part a
+         * compose sequence. */
+        if ((KEV.dwControlKeyState & (LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED))
+            && !(KEV.dwControlKeyState & (LEFT_CTRL_PRESSED |
+            RIGHT_CTRL_PRESSED)) && KEV.bKeyDown) {
+          handle->tty.rd.last_key[0] = '\033';
+          prefix_len = 1;
+        } else {
+          prefix_len = 0;
+        }
+
+        if (KEV.uChar.UnicodeChar >= 0xDC00 &&
+            KEV.uChar.UnicodeChar < 0xE000) {
+          /* UTF-16 surrogate pair */
+          WCHAR utf16_buffer[2];
+          utf16_buffer[0] = handle->tty.rd.last_utf16_high_surrogate;
+          utf16_buffer[1] = KEV.uChar.UnicodeChar;
+          char_len = WideCharToMultiByte(CP_UTF8,
+                                         0,
+                                         utf16_buffer,
+                                         2,
+                                         &handle->tty.rd.last_key[prefix_len],
+                                         sizeof handle->tty.rd.last_key,
+                                         NULL,
+                                         NULL);
+        } else {
+          /* Single UTF-16 character */
+          char_len = WideCharToMultiByte(CP_UTF8,
+                                         0,
+                                         &KEV.uChar.UnicodeChar,
+                                         1,
+                                         &handle->tty.rd.last_key[prefix_len],
+                                         sizeof handle->tty.rd.last_key,
+                                         NULL,
+                                         NULL);
+        }
+
+        /* Whatever happened, the last character wasn't a high surrogate. */
+        handle->tty.rd.last_utf16_high_surrogate = 0;
+
+        /* If the utf16 character(s) couldn't be converted something must be
+         * wrong. */
+        if (!char_len) {
+          handle->flags &= ~UV_HANDLE_READING;
+          DECREASE_ACTIVE_COUNT(loop, handle);
+          handle->read_cb((uv_stream_t*) handle,
+                          uv_translate_sys_error(GetLastError()),
+                          &buf);
+          goto out;
+        }
+
+        handle->tty.rd.last_key_len = (unsigned char) (prefix_len + char_len);
+        handle->tty.rd.last_key_offset = 0;
+        continue;
+
+      } else {
+        /* Function key pressed */
+        const char* vt100;
+        size_t prefix_len, vt100_len;
+
+        vt100 = get_vt100_fn_key(KEV.wVirtualKeyCode,
+                                  !!(KEV.dwControlKeyState & SHIFT_PRESSED),
+                                  !!(KEV.dwControlKeyState & (
+                                    LEFT_CTRL_PRESSED |
+                                    RIGHT_CTRL_PRESSED)),
+                                  &vt100_len);
+
+        /* If we were unable to map to a vt100 sequence, just ignore. */
+        if (!vt100) {
+          continue;
+        }
+
+        /* Prefix with \x033 when the alt key was held. */
+        if (KEV.dwControlKeyState & (LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED)) {
+          handle->tty.rd.last_key[0] = '\033';
+          prefix_len = 1;
+        } else {
+          prefix_len = 0;
+        }
+
+        /* Copy the vt100 sequence to the handle buffer. */
+        assert(prefix_len + vt100_len < sizeof handle->tty.rd.last_key);
+        memcpy(&handle->tty.rd.last_key[prefix_len], vt100, vt100_len);
+
+        handle->tty.rd.last_key_len = (unsigned char) (prefix_len + vt100_len);
+        handle->tty.rd.last_key_offset = 0;
+        continue;
+      }
+    } else {
+      /* Copy any bytes left from the last keypress to the user buffer. */
+      if (handle->tty.rd.last_key_offset < handle->tty.rd.last_key_len) {
+        /* Allocate a buffer if needed */
+        if (buf_used == 0) {
+          buf = uv_buf_init(NULL, 0);
+          handle->alloc_cb((uv_handle_t*) handle, 1024, &buf);
+          if (buf.base == NULL || buf.len == 0) {
+            handle->read_cb((uv_stream_t*) handle, UV_ENOBUFS, &buf);
+            goto out;
+          }
+          assert(buf.base != NULL);
+        }
+
+        buf.base[buf_used++] = handle->tty.rd.last_key[handle->tty.rd.last_key_offset++];
+
+        /* If the buffer is full, emit it */
+        if ((size_t) buf_used == buf.len) {
+          handle->read_cb((uv_stream_t*) handle, buf_used, &buf);
+          buf = uv_null_buf_;
+          buf_used = 0;
+        }
+
+        continue;
+      }
+
+      /* Apply dwRepeat from the last input record. */
+      if (--KEV.wRepeatCount > 0) {
+        handle->tty.rd.last_key_offset = 0;
+        continue;
+      }
+
+      handle->tty.rd.last_key_len = 0;
+      continue;
+    }
+  }
+
+  /* Send the buffer back to the user */
+  if (buf_used > 0) {
+    handle->read_cb((uv_stream_t*) handle, buf_used, &buf);
+  }
+
+ out:
+  /* Wait for more input events. */
+  if ((handle->flags & UV_HANDLE_READING) &&
+      !(handle->flags & UV_HANDLE_READ_PENDING)) {
+    uv__tty_queue_read(loop, handle);
+  }
+
+  DECREASE_PENDING_REQ_COUNT(handle);
+
+#undef KEV
+}
+
+
+
+void uv_process_tty_read_line_req(uv_loop_t* loop, uv_tty_t* handle,
+    uv_req_t* req) {
+  uv_buf_t buf;
+
+  assert(handle->type == UV_TTY);
+  assert(handle->flags & UV_HANDLE_TTY_READABLE);
+
+  buf = handle->tty.rd.read_line_buffer;
+
+  handle->flags &= ~UV_HANDLE_READ_PENDING;
+  handle->tty.rd.read_line_buffer = uv_null_buf_;
+
+  if (!REQ_SUCCESS(req)) {
+    /* Read was not successful */
+    if (handle->flags & UV_HANDLE_READING) {
+      /* Real error */
+      handle->flags &= ~UV_HANDLE_READING;
+      DECREASE_ACTIVE_COUNT(loop, handle);
+      handle->read_cb((uv_stream_t*) handle,
+                      uv_translate_sys_error(GET_REQ_ERROR(req)),
+                      &buf);
+    }
+  } else {
+    if (!(handle->flags & UV_HANDLE_CANCELLATION_PENDING) &&
+        req->u.io.overlapped.InternalHigh != 0) {
+      /* Read successful. TODO: read unicode, convert to utf-8 */
+      DWORD bytes = req->u.io.overlapped.InternalHigh;
+      handle->read_cb((uv_stream_t*) handle, bytes, &buf);
+    }
+    handle->flags &= ~UV_HANDLE_CANCELLATION_PENDING;
+  }
+
+  /* Wait for more input events. */
+  if ((handle->flags & UV_HANDLE_READING) &&
+      !(handle->flags & UV_HANDLE_READ_PENDING)) {
+    uv__tty_queue_read(loop, handle);
+  }
+
+  DECREASE_PENDING_REQ_COUNT(handle);
+}
+
+
+void uv__process_tty_read_req(uv_loop_t* loop, uv_tty_t* handle,
+    uv_req_t* req) {
+  assert(handle->type == UV_TTY);
+  assert(handle->flags & UV_HANDLE_TTY_READABLE);
+
+  /* If the read_line_buffer member is zero, it must have been an raw read.
+   * Otherwise it was a line-buffered read. FIXME: This is quite obscure. Use a
+   * flag or something. */
+  if (handle->tty.rd.read_line_buffer.len == 0) {
+    uv_process_tty_read_raw_req(loop, handle, req);
+  } else {
+    uv_process_tty_read_line_req(loop, handle, req);
+  }
+}
+
+
+int uv__tty_read_start(uv_tty_t* handle, uv_alloc_cb alloc_cb,
+    uv_read_cb read_cb) {
+  uv_loop_t* loop = handle->loop;
+
+  if (!(handle->flags & UV_HANDLE_TTY_READABLE)) {
+    return ERROR_INVALID_PARAMETER;
+  }
+
+  handle->flags |= UV_HANDLE_READING;
+  INCREASE_ACTIVE_COUNT(loop, handle);
+  handle->read_cb = read_cb;
+  handle->alloc_cb = alloc_cb;
+
+  /* If reading was stopped and then started again, there could still be a read
+   * request pending. */
+  if (handle->flags & UV_HANDLE_READ_PENDING) {
+    return 0;
+  }
+
+  /* Maybe the user stopped reading half-way while processing key events.
+   * Short-circuit if this could be the case. */
+  if (handle->tty.rd.last_key_len > 0) {
+    SET_REQ_SUCCESS(&handle->read_req);
+    uv__insert_pending_req(handle->loop, (uv_req_t*) &handle->read_req);
+    /* Make sure no attempt is made to insert it again until it's handled. */
+    handle->flags |= UV_HANDLE_READ_PENDING;
+    handle->reqs_pending++;
+    return 0;
+  }
+
+  uv__tty_queue_read(loop, handle);
+
+  return 0;
+}
+
+
+int uv__tty_read_stop(uv_tty_t* handle) {
+  INPUT_RECORD record;
+  DWORD written, err;
+
+  handle->flags &= ~UV_HANDLE_READING;
+  DECREASE_ACTIVE_COUNT(handle->loop, handle);
+
+  if (!(handle->flags & UV_HANDLE_READ_PENDING))
+    return 0;
+
+  if (handle->flags & UV_HANDLE_TTY_RAW) {
+    /* Cancel raw read. Write some bullshit event to force the console wait to
+     * return. */
+    memset(&record, 0, sizeof record);
+    record.EventType = FOCUS_EVENT;
+    if (!WriteConsoleInputW(handle->handle, &record, 1, &written)) {
+      return GetLastError();
+    }
+  } else if (!(handle->flags & UV_HANDLE_CANCELLATION_PENDING)) {
+    /* Cancel line-buffered read if not already pending */
+    err = uv__cancel_read_console(handle);
+    if (err)
+      return err;
+
+    handle->flags |= UV_HANDLE_CANCELLATION_PENDING;
+  }
+
+  return 0;
+}
+
+static int uv__cancel_read_console(uv_tty_t* handle) {
+  HANDLE active_screen_buffer = INVALID_HANDLE_VALUE;
+  INPUT_RECORD record;
+  DWORD written;
+  DWORD err = 0;
+  LONG status;
+
+  assert(!(handle->flags & UV_HANDLE_CANCELLATION_PENDING));
+
+  /* Hold the output lock during the cancellation, to ensure that further
+     writes don't interfere with the screen state. It will be the ReadConsole
+     thread's responsibility to release the lock. */
+  uv_sem_wait(&uv_tty_output_lock);
+  status = InterlockedExchange(&uv__read_console_status, TRAP_REQUESTED);
+  if (status != IN_PROGRESS) {
+    /* Either we have managed to set a trap for the other thread before
+       ReadConsole is called, or ReadConsole has returned because the user
+       has pressed ENTER. In either case, there is nothing else to do. */
+    uv_sem_post(&uv_tty_output_lock);
+    return 0;
+  }
+
+  /* Save screen state before sending the VK_RETURN event */
+  active_screen_buffer = CreateFileA("conout$",
+                                     GENERIC_READ | GENERIC_WRITE,
+                                     FILE_SHARE_READ | FILE_SHARE_WRITE,
+                                     NULL,
+                                     OPEN_EXISTING,
+                                     FILE_ATTRIBUTE_NORMAL,
+                                     NULL);
+
+  if (active_screen_buffer != INVALID_HANDLE_VALUE &&
+      GetConsoleScreenBufferInfo(active_screen_buffer,
+                                 &uv__saved_screen_state)) {
+    InterlockedOr(&uv__restore_screen_state, 1);
+  }
+
+  /* Write enter key event to force the console wait to return. */
+  record.EventType = KEY_EVENT;
+  record.Event.KeyEvent.bKeyDown = TRUE;
+  record.Event.KeyEvent.wRepeatCount = 1;
+  record.Event.KeyEvent.wVirtualKeyCode = VK_RETURN;
+  record.Event.KeyEvent.wVirtualScanCode =
+    MapVirtualKeyW(VK_RETURN, MAPVK_VK_TO_VSC);
+  record.Event.KeyEvent.uChar.UnicodeChar = L'\r';
+  record.Event.KeyEvent.dwControlKeyState = 0;
+  if (!WriteConsoleInputW(handle->handle, &record, 1, &written))
+    err = GetLastError();
+
+  if (active_screen_buffer != INVALID_HANDLE_VALUE)
+    CloseHandle(active_screen_buffer);
+
+  return err;
+}
+
+
+static void uv__tty_update_virtual_window(CONSOLE_SCREEN_BUFFER_INFO* info) {
+  uv_tty_virtual_width = info->dwSize.X;
+  uv_tty_virtual_height = info->srWindow.Bottom - info->srWindow.Top + 1;
+
+  /* Recompute virtual window offset row. */
+  if (uv_tty_virtual_offset == -1) {
+    uv_tty_virtual_offset = info->dwCursorPosition.Y;
+  } else if (uv_tty_virtual_offset < info->dwCursorPosition.Y -
+             uv_tty_virtual_height + 1) {
+    /* If suddenly find the cursor outside of the virtual window, it must have
+     * somehow scrolled. Update the virtual window offset. */
+    uv_tty_virtual_offset = info->dwCursorPosition.Y -
+                            uv_tty_virtual_height + 1;
+  }
+  if (uv_tty_virtual_offset + uv_tty_virtual_height > info->dwSize.Y) {
+    uv_tty_virtual_offset = info->dwSize.Y - uv_tty_virtual_height;
+  }
+  if (uv_tty_virtual_offset < 0) {
+    uv_tty_virtual_offset = 0;
+  }
+}
+
+
+static COORD uv__tty_make_real_coord(uv_tty_t* handle,
+    CONSOLE_SCREEN_BUFFER_INFO* info, int x, unsigned char x_relative, int y,
+    unsigned char y_relative) {
+  COORD result;
+
+  uv__tty_update_virtual_window(info);
+
+  /* Adjust y position */
+  if (y_relative) {
+    y = info->dwCursorPosition.Y + y;
+  } else {
+    y = uv_tty_virtual_offset + y;
+  }
+  /* Clip y to virtual client rectangle */
+  if (y < uv_tty_virtual_offset) {
+    y = uv_tty_virtual_offset;
+  } else if (y >= uv_tty_virtual_offset + uv_tty_virtual_height) {
+    y = uv_tty_virtual_offset + uv_tty_virtual_height - 1;
+  }
+
+  /* Adjust x */
+  if (x_relative) {
+    x = info->dwCursorPosition.X + x;
+  }
+  /* Clip x */
+  if (x < 0) {
+    x = 0;
+  } else if (x >= uv_tty_virtual_width) {
+    x = uv_tty_virtual_width - 1;
+  }
+
+  result.X = (unsigned short) x;
+  result.Y = (unsigned short) y;
+  return result;
+}
+
+
+static int uv__tty_emit_text(uv_tty_t* handle, WCHAR buffer[], DWORD length,
+    DWORD* error) {
+  DWORD written;
+
+  if (*error != ERROR_SUCCESS) {
+    return -1;
+  }
+
+  if (!WriteConsoleW(handle->handle,
+                     (void*) buffer,
+                     length,
+                     &written,
+                     NULL)) {
+    *error = GetLastError();
+    return -1;
+  }
+
+  return 0;
+}
+
+
+static int uv__tty_move_caret(uv_tty_t* handle, int x, unsigned char x_relative,
+    int y, unsigned char y_relative, DWORD* error) {
+  CONSOLE_SCREEN_BUFFER_INFO info;
+  COORD pos;
+
+  if (*error != ERROR_SUCCESS) {
+    return -1;
+  }
+
+ retry:
+  if (!GetConsoleScreenBufferInfo(handle->handle, &info)) {
+    *error = GetLastError();
+  }
+
+  pos = uv__tty_make_real_coord(handle, &info, x, x_relative, y, y_relative);
+
+  if (!SetConsoleCursorPosition(handle->handle, pos)) {
+    if (GetLastError() == ERROR_INVALID_PARAMETER) {
+      /* The console may be resized - retry */
+      goto retry;
+    } else {
+      *error = GetLastError();
+      return -1;
+    }
+  }
+
+  return 0;
+}
+
+
+static int uv__tty_reset(uv_tty_t* handle, DWORD* error) {
+  const COORD origin = {0, 0};
+  const WORD char_attrs = uv_tty_default_text_attributes;
+  CONSOLE_SCREEN_BUFFER_INFO screen_buffer_info;
+  DWORD count, written;
+
+  if (*error != ERROR_SUCCESS) {
+    return -1;
+  }
+
+  /* Reset original text attributes. */
+  if (!SetConsoleTextAttribute(handle->handle, char_attrs)) {
+    *error = GetLastError();
+    return -1;
+  }
+
+  /* Move the cursor position to (0, 0). */
+  if (!SetConsoleCursorPosition(handle->handle, origin)) {
+    *error = GetLastError();
+    return -1;
+  }
+
+  /* Clear the screen buffer. */
+ retry:
+   if (!GetConsoleScreenBufferInfo(handle->handle, &screen_buffer_info)) {
+     *error = GetLastError();
+     return -1;
+  }
+
+  count = screen_buffer_info.dwSize.X * screen_buffer_info.dwSize.Y;
+
+  if (!(FillConsoleOutputCharacterW(handle->handle,
+                                    L'\x20',
+                                    count,
+                                    origin,
+                                    &written) &&
+        FillConsoleOutputAttribute(handle->handle,
+                                   char_attrs,
+                                   written,
+                                   origin,
+                                   &written))) {
+    if (GetLastError() == ERROR_INVALID_PARAMETER) {
+      /* The console may be resized - retry */
+      goto retry;
+    } else {
+      *error = GetLastError();
+      return -1;
+    }
+  }
+
+  /* Move the virtual window up to the top. */
+  uv_tty_virtual_offset = 0;
+  uv__tty_update_virtual_window(&screen_buffer_info);
+
+  /* Reset the cursor size and the cursor state. */
+  if (!SetConsoleCursorInfo(handle->handle, &uv_tty_default_cursor_info)) {
+    *error = GetLastError();
+    return -1;
+  }
+
+  return 0;
+}
+
+
+static int uv__tty_clear(uv_tty_t* handle, int dir, char entire_screen,
+    DWORD* error) {
+  CONSOLE_SCREEN_BUFFER_INFO info;
+  COORD start, end;
+  DWORD count, written;
+
+  int x1, x2, y1, y2;
+  int x1r, x2r, y1r, y2r;
+
+  if (*error != ERROR_SUCCESS) {
+    return -1;
+  }
+
+  if (dir == 0) {
+    /* Clear from current position */
+    x1 = 0;
+    x1r = 1;
+  } else {
+    /* Clear from column 0 */
+    x1 = 0;
+    x1r = 0;
+  }
+
+  if (dir == 1) {
+    /* Clear to current position */
+    x2 = 0;
+    x2r = 1;
+  } else {
+    /* Clear to end of row. We pretend the console is 65536 characters wide,
+     * uv__tty_make_real_coord will clip it to the actual console width. */
+    x2 = 0xffff;
+    x2r = 0;
+  }
+
+  if (!entire_screen) {
+    /* Stay on our own row */
+    y1 = y2 = 0;
+    y1r = y2r = 1;
+  } else {
+    /* Apply columns direction to row */
+    y1 = x1;
+    y1r = x1r;
+    y2 = x2;
+    y2r = x2r;
+  }
+
+ retry:
+  if (!GetConsoleScreenBufferInfo(handle->handle, &info)) {
+    *error = GetLastError();
+    return -1;
+  }
+
+  start = uv__tty_make_real_coord(handle, &info, x1, x1r, y1, y1r);
+  end = uv__tty_make_real_coord(handle, &info, x2, x2r, y2, y2r);
+  count = (end.Y * info.dwSize.X + end.X) -
+          (start.Y * info.dwSize.X + start.X) + 1;
+
+  if (!(FillConsoleOutputCharacterW(handle->handle,
+                              L'\x20',
+                              count,
+                              start,
+                              &written) &&
+        FillConsoleOutputAttribute(handle->handle,
+                                   info.wAttributes,
+                                   written,
+                                   start,
+                                   &written))) {
+    if (GetLastError() == ERROR_INVALID_PARAMETER) {
+      /* The console may be resized - retry */
+      goto retry;
+    } else {
+      *error = GetLastError();
+      return -1;
+    }
+  }
+
+  return 0;
+}
+
+#define FLIP_FGBG                                                             \
+    do {                                                                      \
+      WORD fg = info.wAttributes & 0xF;                                       \
+      WORD bg = info.wAttributes & 0xF0;                                      \
+      info.wAttributes &= 0xFF00;                                             \
+      info.wAttributes |= fg << 4;                                            \
+      info.wAttributes |= bg >> 4;                                            \
+    } while (0)
+
+static int uv__tty_set_style(uv_tty_t* handle, DWORD* error) {
+  unsigned short argc = handle->tty.wr.ansi_csi_argc;
+  unsigned short* argv = handle->tty.wr.ansi_csi_argv;
+  int i;
+  CONSOLE_SCREEN_BUFFER_INFO info;
+
+  char fg_color = -1, bg_color = -1;
+  char fg_bright = -1, bg_bright = -1;
+  char inverse = -1;
+
+  if (argc == 0) {
+    /* Reset mode */
+    fg_color = uv_tty_default_fg_color;
+    bg_color = uv_tty_default_bg_color;
+    fg_bright = uv_tty_default_fg_bright;
+    bg_bright = uv_tty_default_bg_bright;
+    inverse = uv_tty_default_inverse;
+  }
+
+  for (i = 0; i < argc; i++) {
+    short arg = argv[i];
+
+    if (arg == 0) {
+      /* Reset mode */
+      fg_color = uv_tty_default_fg_color;
+      bg_color = uv_tty_default_bg_color;
+      fg_bright = uv_tty_default_fg_bright;
+      bg_bright = uv_tty_default_bg_bright;
+      inverse = uv_tty_default_inverse;
+
+    } else if (arg == 1) {
+      /* Foreground bright on */
+      fg_bright = 1;
+
+    } else if (arg == 2) {
+      /* Both bright off */
+      fg_bright = 0;
+      bg_bright = 0;
+
+    } else if (arg == 5) {
+      /* Background bright on */
+      bg_bright = 1;
+
+    } else if (arg == 7) {
+      /* Inverse: on */
+      inverse = 1;
+
+    } else if (arg == 21 || arg == 22) {
+      /* Foreground bright off */
+      fg_bright = 0;
+
+    } else if (arg == 25) {
+      /* Background bright off */
+      bg_bright = 0;
+
+    } else if (arg == 27) {
+      /* Inverse: off */
+      inverse = 0;
+
+    } else if (arg >= 30 && arg <= 37) {
+      /* Set foreground color */
+      fg_color = arg - 30;
+
+    } else if (arg == 39) {
+      /* Default text color */
+      fg_color = uv_tty_default_fg_color;
+      fg_bright = uv_tty_default_fg_bright;
+
+    } else if (arg >= 40 && arg <= 47) {
+      /* Set background color */
+      bg_color = arg - 40;
+
+    } else if (arg ==  49) {
+      /* Default background color */
+      bg_color = uv_tty_default_bg_color;
+      bg_bright = uv_tty_default_bg_bright;
+
+    } else if (arg >= 90 && arg <= 97) {
+      /* Set bold foreground color */
+      fg_bright = 1;
+      fg_color = arg - 90;
+
+    } else if (arg >= 100 && arg <= 107) {
+      /* Set bold background color */
+      bg_bright = 1;
+      bg_color = arg - 100;
+
+    }
+  }
+
+  if (fg_color == -1 && bg_color == -1 && fg_bright == -1 &&
+      bg_bright == -1 && inverse == -1) {
+    /* Nothing changed */
+    return 0;
+  }
+
+  if (!GetConsoleScreenBufferInfo(handle->handle, &info)) {
+    *error = GetLastError();
+    return -1;
+  }
+
+  if ((info.wAttributes & COMMON_LVB_REVERSE_VIDEO) > 0) {
+    FLIP_FGBG;
+  }
+
+  if (fg_color != -1) {
+    info.wAttributes &= ~(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
+    if (fg_color & 1) info.wAttributes |= FOREGROUND_RED;
+    if (fg_color & 2) info.wAttributes |= FOREGROUND_GREEN;
+    if (fg_color & 4) info.wAttributes |= FOREGROUND_BLUE;
+  }
+
+  if (fg_bright != -1) {
+    if (fg_bright) {
+      info.wAttributes |= FOREGROUND_INTENSITY;
+    } else {
+      info.wAttributes &= ~FOREGROUND_INTENSITY;
+    }
+  }
+
+  if (bg_color != -1) {
+    info.wAttributes &= ~(BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE);
+    if (bg_color & 1) info.wAttributes |= BACKGROUND_RED;
+    if (bg_color & 2) info.wAttributes |= BACKGROUND_GREEN;
+    if (bg_color & 4) info.wAttributes |= BACKGROUND_BLUE;
+  }
+
+  if (bg_bright != -1) {
+    if (bg_bright) {
+      info.wAttributes |= BACKGROUND_INTENSITY;
+    } else {
+      info.wAttributes &= ~BACKGROUND_INTENSITY;
+    }
+  }
+
+  if (inverse != -1) {
+    if (inverse) {
+      info.wAttributes |= COMMON_LVB_REVERSE_VIDEO;
+    } else {
+      info.wAttributes &= ~COMMON_LVB_REVERSE_VIDEO;
+    }
+  }
+
+  if ((info.wAttributes & COMMON_LVB_REVERSE_VIDEO) > 0) {
+    FLIP_FGBG;
+  }
+
+  if (!SetConsoleTextAttribute(handle->handle, info.wAttributes)) {
+    *error = GetLastError();
+    return -1;
+  }
+
+  return 0;
+}
+
+
+static int uv__tty_save_state(uv_tty_t* handle, unsigned char save_attributes,
+    DWORD* error) {
+  CONSOLE_SCREEN_BUFFER_INFO info;
+
+  if (*error != ERROR_SUCCESS) {
+    return -1;
+  }
+
+  if (!GetConsoleScreenBufferInfo(handle->handle, &info)) {
+    *error = GetLastError();
+    return -1;
+  }
+
+  uv__tty_update_virtual_window(&info);
+
+  handle->tty.wr.saved_position.X = info.dwCursorPosition.X;
+  handle->tty.wr.saved_position.Y = info.dwCursorPosition.Y -
+        uv_tty_virtual_offset;
+  handle->flags |= UV_HANDLE_TTY_SAVED_POSITION;
+
+  if (save_attributes) {
+    handle->tty.wr.saved_attributes = info.wAttributes &
+        (FOREGROUND_INTENSITY | BACKGROUND_INTENSITY);
+    handle->flags |= UV_HANDLE_TTY_SAVED_ATTRIBUTES;
+  }
+
+  return 0;
+}
+
+
+static int uv__tty_restore_state(uv_tty_t* handle,
+    unsigned char restore_attributes, DWORD* error) {
+  CONSOLE_SCREEN_BUFFER_INFO info;
+  WORD new_attributes;
+
+  if (*error != ERROR_SUCCESS) {
+    return -1;
+  }
+
+  if (handle->flags & UV_HANDLE_TTY_SAVED_POSITION) {
+    if (uv__tty_move_caret(handle,
+                          handle->tty.wr.saved_position.X,
+                          0,
+                          handle->tty.wr.saved_position.Y,
+                          0,
+                          error) != 0) {
+      return -1;
+    }
+  }
+
+  if (restore_attributes &&
+      (handle->flags & UV_HANDLE_TTY_SAVED_ATTRIBUTES)) {
+    if (!GetConsoleScreenBufferInfo(handle->handle, &info)) {
+      *error = GetLastError();
+      return -1;
+    }
+
+    new_attributes = info.wAttributes;
+    new_attributes &= ~(FOREGROUND_INTENSITY | BACKGROUND_INTENSITY);
+    new_attributes |= handle->tty.wr.saved_attributes;
+
+    if (!SetConsoleTextAttribute(handle->handle, new_attributes)) {
+      *error = GetLastError();
+      return -1;
+    }
+  }
+
+  return 0;
+}
+
+static int uv__tty_set_cursor_visibility(uv_tty_t* handle,
+                                        BOOL visible,
+                                        DWORD* error) {
+  CONSOLE_CURSOR_INFO cursor_info;
+
+  if (!GetConsoleCursorInfo(handle->handle, &cursor_info)) {
+    *error = GetLastError();
+    return -1;
+  }
+
+  cursor_info.bVisible = visible;
+
+  if (!SetConsoleCursorInfo(handle->handle, &cursor_info)) {
+    *error = GetLastError();
+    return -1;
+  }
+
+  return 0;
+}
+
+static int uv__tty_set_cursor_shape(uv_tty_t* handle, int style, DWORD* error) {
+  CONSOLE_CURSOR_INFO cursor_info;
+
+  if (!GetConsoleCursorInfo(handle->handle, &cursor_info)) {
+    *error = GetLastError();
+    return -1;
+  }
+
+  if (style == 0) {
+    cursor_info.dwSize = uv_tty_default_cursor_info.dwSize;
+  } else if (style <= 2) {
+    cursor_info.dwSize = CURSOR_SIZE_LARGE;
+  } else {
+    cursor_info.dwSize = CURSOR_SIZE_SMALL;
+  }
+
+  if (!SetConsoleCursorInfo(handle->handle, &cursor_info)) {
+    *error = GetLastError();
+    return -1;
+  }
+
+  return 0;
+}
+
+
+static int uv__tty_write_bufs(uv_tty_t* handle,
+                             const uv_buf_t bufs[],
+                             unsigned int nbufs,
+                             DWORD* error) {
+  /* We can only write 8k characters at a time. Windows can't handle much more
+   * characters in a single console write anyway. */
+  WCHAR utf16_buf[MAX_CONSOLE_CHAR];
+  DWORD utf16_buf_used = 0;
+  unsigned int i;
+
+#define FLUSH_TEXT()                                                \
+  do {                                                              \
+    if (utf16_buf_used > 0) {                                       \
+      uv__tty_emit_text(handle, utf16_buf, utf16_buf_used, error);  \
+      utf16_buf_used = 0;                                           \
+    }                                                               \
+  } while (0)
+
+#define ENSURE_BUFFER_SPACE(wchars_needed)                          \
+  if (wchars_needed > ARRAY_SIZE(utf16_buf) - utf16_buf_used) {     \
+    FLUSH_TEXT();                                                   \
+  }
+
+  /* Cache for fast access */
+  unsigned char utf8_bytes_left = handle->tty.wr.utf8_bytes_left;
+  unsigned int utf8_codepoint = handle->tty.wr.utf8_codepoint;
+  unsigned char previous_eol = handle->tty.wr.previous_eol;
+  unsigned short ansi_parser_state = handle->tty.wr.ansi_parser_state;
+
+  /* Store the error here. If we encounter an error, stop trying to do i/o but
+   * keep parsing the buffer so we leave the parser in a consistent state. */
+  *error = ERROR_SUCCESS;
+
+  uv_sem_wait(&uv_tty_output_lock);
+
+  for (i = 0; i < nbufs; i++) {
+    uv_buf_t buf = bufs[i];
+    unsigned int j;
+
+    for (j = 0; j < buf.len; j++) {
+      unsigned char c = buf.base[j];
+
+      /* Run the character through the utf8 decoder We happily accept non
+       * shortest form encodings and invalid code points - there's no real harm
+       * that can be done. */
+      if (utf8_bytes_left == 0) {
+        /* Read utf-8 start byte */
+        DWORD first_zero_bit;
+        unsigned char not_c = ~c;
+#ifdef _MSC_VER /* msvc */
+        if (_BitScanReverse(&first_zero_bit, not_c)) {
+#else /* assume gcc */
+        if (c != 0) {
+          first_zero_bit = (sizeof(int) * 8) - 1 - __builtin_clz(not_c);
+#endif
+          if (first_zero_bit == 7) {
+            /* Ascii - pass right through */
+            utf8_codepoint = (unsigned int) c;
+
+          } else if (first_zero_bit <= 5) {
+            /* Multibyte sequence */
+            utf8_codepoint = (0xff >> (8 - first_zero_bit)) & c;
+            utf8_bytes_left = (char) (6 - first_zero_bit);
+
+          } else {
+            /* Invalid continuation */
+            utf8_codepoint = UNICODE_REPLACEMENT_CHARACTER;
+          }
+
+        } else {
+          /* 0xff -- invalid */
+          utf8_codepoint = UNICODE_REPLACEMENT_CHARACTER;
+        }
+
+      } else if ((c & 0xc0) == 0x80) {
+        /* Valid continuation of utf-8 multibyte sequence */
+        utf8_bytes_left--;
+        utf8_codepoint <<= 6;
+        utf8_codepoint |= ((unsigned int) c & 0x3f);
+
+      } else {
+        /* Start byte where continuation was expected. */
+        utf8_bytes_left = 0;
+        utf8_codepoint = UNICODE_REPLACEMENT_CHARACTER;
+        /* Patch buf offset so this character will be parsed again as a start
+         * byte. */
+        j--;
+      }
+
+      /* Maybe we need to parse more bytes to find a character. */
+      if (utf8_bytes_left != 0) {
+        continue;
+      }
+
+      /* Parse vt100/ansi escape codes */
+      if (uv__vterm_state == UV_TTY_SUPPORTED) {
+        /* Pass through escape codes if conhost supports them. */
+      } else if (ansi_parser_state == ANSI_NORMAL) {
+        switch (utf8_codepoint) {
+          case '\033':
+            ansi_parser_state = ANSI_ESCAPE_SEEN;
+            continue;
+
+          case 0233:
+            ansi_parser_state = ANSI_CSI;
+            handle->tty.wr.ansi_csi_argc = 0;
+            continue;
+        }
+
+      } else if (ansi_parser_state == ANSI_ESCAPE_SEEN) {
+        switch (utf8_codepoint) {
+          case '[':
+            ansi_parser_state = ANSI_CSI;
+            handle->tty.wr.ansi_csi_argc = 0;
+            continue;
+
+          case '^':
+          case '_':
+          case 'P':
+          case ']':
+            /* Not supported, but we'll have to parse until we see a stop code,
+             * e. g. ESC \ or BEL. */
+            ansi_parser_state = ANSI_ST_CONTROL;
+            continue;
+
+          case '\033':
+            /* Ignore double escape. */
+            continue;
+
+          case 'c':
+            /* Full console reset. */
+            FLUSH_TEXT();
+            uv__tty_reset(handle, error);
+            ansi_parser_state = ANSI_NORMAL;
+            continue;
+
+          case '7':
+            /* Save the cursor position and text attributes. */
+            FLUSH_TEXT();
+            uv__tty_save_state(handle, 1, error);
+            ansi_parser_state = ANSI_NORMAL;
+            continue;
+
+          case '8':
+            /* Restore the cursor position and text attributes */
+            FLUSH_TEXT();
+            uv__tty_restore_state(handle, 1, error);
+            ansi_parser_state = ANSI_NORMAL;
+            continue;
+
+          default:
+            if (utf8_codepoint >= '@' && utf8_codepoint <= '_') {
+              /* Single-char control. */
+              ansi_parser_state = ANSI_NORMAL;
+              continue;
+            } else {
+              /* Invalid - proceed as normal, */
+              ansi_parser_state = ANSI_NORMAL;
+            }
+        }
+
+      } else if (ansi_parser_state == ANSI_IGNORE) {
+        /* We're ignoring this command. Stop only on command character. */
+        if (utf8_codepoint >= '@' && utf8_codepoint <= '~') {
+          ansi_parser_state = ANSI_NORMAL;
+        }
+        continue;
+
+      } else if (ansi_parser_state == ANSI_DECSCUSR) {
+        /* So far we've the sequence `ESC [ arg space`, and we're waiting for
+         * the final command byte. */
+        if (utf8_codepoint >= '@' && utf8_codepoint <= '~') {
+          /* Command byte */
+          if (utf8_codepoint == 'q') {
+            /* Change the cursor shape */
+            int style = handle->tty.wr.ansi_csi_argc
+              ? handle->tty.wr.ansi_csi_argv[0] : 1;
+            if (style >= 0 && style <= 6) {
+              FLUSH_TEXT();
+              uv__tty_set_cursor_shape(handle, style, error);
+            }
+          }
+
+          /* Sequence ended - go back to normal state. */
+          ansi_parser_state = ANSI_NORMAL;
+          continue;
+        }
+        /* Unexpected character, but sequence hasn't ended yet. Ignore the rest
+         * of the sequence. */
+        ansi_parser_state = ANSI_IGNORE;
+
+      } else if (ansi_parser_state & ANSI_CSI) {
+        /* So far we've seen `ESC [`, and we may or may not have already parsed
+         * some of the arguments that follow. */
+
+        if (utf8_codepoint >= '0' && utf8_codepoint <= '9') {
+          /* Parse a numerical argument. */
+          if (!(ansi_parser_state & ANSI_IN_ARG)) {
+            /* We were not currently parsing a number, add a new one. */
+            /* Check for that there are too many arguments. */
+            if (handle->tty.wr.ansi_csi_argc >=
+                ARRAY_SIZE(handle->tty.wr.ansi_csi_argv)) {
+              ansi_parser_state = ANSI_IGNORE;
+              continue;
+            }
+            ansi_parser_state |= ANSI_IN_ARG;
+            handle->tty.wr.ansi_csi_argc++;
+            handle->tty.wr.ansi_csi_argv[handle->tty.wr.ansi_csi_argc - 1] =
+                (unsigned short) utf8_codepoint - '0';
+            continue;
+
+          } else {
+            /* We were already parsing a number. Parse next digit. */
+            uint32_t value = 10 *
+                handle->tty.wr.ansi_csi_argv[handle->tty.wr.ansi_csi_argc - 1];
+
+            /* Check for overflow. */
+            if (value > UINT16_MAX) {
+              ansi_parser_state = ANSI_IGNORE;
+              continue;
+            }
+
+            handle->tty.wr.ansi_csi_argv[handle->tty.wr.ansi_csi_argc - 1] =
+                (unsigned short) value + (utf8_codepoint - '0');
+            continue;
+          }
+
+        } else if (utf8_codepoint == ';') {
+          /* Denotes the end of an argument. */
+          if (ansi_parser_state & ANSI_IN_ARG) {
+            ansi_parser_state &= ~ANSI_IN_ARG;
+            continue;
+
+          } else {
+            /* If ANSI_IN_ARG is not set, add another argument and default
+             * it to 0. */
+
+            /* Check for too many arguments */
+            if (handle->tty.wr.ansi_csi_argc >=
+
+                ARRAY_SIZE(handle->tty.wr.ansi_csi_argv)) {
+              ansi_parser_state = ANSI_IGNORE;
+              continue;
+            }
+
+            handle->tty.wr.ansi_csi_argc++;
+            handle->tty.wr.ansi_csi_argv[handle->tty.wr.ansi_csi_argc - 1] = 0;
+            continue;
+          }
+
+        } else if (utf8_codepoint == '?' &&
+                   !(ansi_parser_state & ANSI_IN_ARG) &&
+                   !(ansi_parser_state & ANSI_EXTENSION) &&
+                   handle->tty.wr.ansi_csi_argc == 0) {
+          /* Pass through '?' if it is the first character after CSI */
+          /* This is an extension character from the VT100 codeset */
+          /* that is supported and used by most ANSI terminals today. */
+          ansi_parser_state |= ANSI_EXTENSION;
+          continue;
+
+        } else if (utf8_codepoint == ' ' &&
+                   !(ansi_parser_state & ANSI_EXTENSION)) {
+          /* We expect a command byte to follow after this space. The only
+           * command that we current support is 'set cursor style'. */
+          ansi_parser_state = ANSI_DECSCUSR;
+          continue;
+
+        } else if (utf8_codepoint >= '@' && utf8_codepoint <= '~') {
+          /* Command byte */
+          if (ansi_parser_state & ANSI_EXTENSION) {
+            /* Sequence is `ESC [ ? args command`. */
+            switch (utf8_codepoint) {
+              case 'l':
+                /* Hide the cursor */
+                if (handle->tty.wr.ansi_csi_argc == 1 &&
+                    handle->tty.wr.ansi_csi_argv[0] == 25) {
+                  FLUSH_TEXT();
+                  uv__tty_set_cursor_visibility(handle, 0, error);
+                }
+                break;
+
+              case 'h':
+                /* Show the cursor */
+                if (handle->tty.wr.ansi_csi_argc == 1 &&
+                    handle->tty.wr.ansi_csi_argv[0] == 25) {
+                  FLUSH_TEXT();
+                  uv__tty_set_cursor_visibility(handle, 1, error);
+                }
+                break;
+            }
+
+          } else {
+            /* Sequence is `ESC [ args command`. */
+            int x, y, d;
+            switch (utf8_codepoint) {
+              case 'A':
+                /* cursor up */
+                FLUSH_TEXT();
+                y = -(handle->tty.wr.ansi_csi_argc
+                  ? handle->tty.wr.ansi_csi_argv[0] : 1);
+                uv__tty_move_caret(handle, 0, 1, y, 1, error);
+                break;
+
+              case 'B':
+                /* cursor down */
+                FLUSH_TEXT();
+                y = handle->tty.wr.ansi_csi_argc
+                  ? handle->tty.wr.ansi_csi_argv[0] : 1;
+                uv__tty_move_caret(handle, 0, 1, y, 1, error);
+                break;
+
+              case 'C':
+                /* cursor forward */
+                FLUSH_TEXT();
+                x = handle->tty.wr.ansi_csi_argc
+                  ? handle->tty.wr.ansi_csi_argv[0] : 1;
+                uv__tty_move_caret(handle, x, 1, 0, 1, error);
+                break;
+
+              case 'D':
+                /* cursor back */
+                FLUSH_TEXT();
+                x = -(handle->tty.wr.ansi_csi_argc
+                  ? handle->tty.wr.ansi_csi_argv[0] : 1);
+                uv__tty_move_caret(handle, x, 1, 0, 1, error);
+                break;
+
+              case 'E':
+                /* cursor next line */
+                FLUSH_TEXT();
+                y = handle->tty.wr.ansi_csi_argc
+                  ? handle->tty.wr.ansi_csi_argv[0] : 1;
+                uv__tty_move_caret(handle, 0, 0, y, 1, error);
+                break;
+
+              case 'F':
+                /* cursor previous line */
+                FLUSH_TEXT();
+                y = -(handle->tty.wr.ansi_csi_argc
+                  ? handle->tty.wr.ansi_csi_argv[0] : 1);
+                uv__tty_move_caret(handle, 0, 0, y, 1, error);
+                break;
+
+              case 'G':
+                /* cursor horizontal move absolute */
+                FLUSH_TEXT();
+                x = (handle->tty.wr.ansi_csi_argc >= 1 &&
+                     handle->tty.wr.ansi_csi_argv[0])
+                  ? handle->tty.wr.ansi_csi_argv[0] - 1 : 0;
+                uv__tty_move_caret(handle, x, 0, 0, 1, error);
+                break;
+
+              case 'H':
+              case 'f':
+                /* cursor move absolute */
+                FLUSH_TEXT();
+                y = (handle->tty.wr.ansi_csi_argc >= 1 &&
+                     handle->tty.wr.ansi_csi_argv[0])
+                  ? handle->tty.wr.ansi_csi_argv[0] - 1 : 0;
+                x = (handle->tty.wr.ansi_csi_argc >= 2 &&
+                     handle->tty.wr.ansi_csi_argv[1])
+                  ? handle->tty.wr.ansi_csi_argv[1] - 1 : 0;
+                uv__tty_move_caret(handle, x, 0, y, 0, error);
+                break;
+
+              case 'J':
+                /* Erase screen */
+                FLUSH_TEXT();
+                d = handle->tty.wr.ansi_csi_argc
+                  ? handle->tty.wr.ansi_csi_argv[0] : 0;
+                if (d >= 0 && d <= 2) {
+                  uv__tty_clear(handle, d, 1, error);
+                }
+                break;
+
+              case 'K':
+                /* Erase line */
+                FLUSH_TEXT();
+                d = handle->tty.wr.ansi_csi_argc
+                  ? handle->tty.wr.ansi_csi_argv[0] : 0;
+                if (d >= 0 && d <= 2) {
+                  uv__tty_clear(handle, d, 0, error);
+                }
+                break;
+
+              case 'm':
+                /* Set style */
+                FLUSH_TEXT();
+                uv__tty_set_style(handle, error);
+                break;
+
+              case 's':
+                /* Save the cursor position. */
+                FLUSH_TEXT();
+                uv__tty_save_state(handle, 0, error);
+                break;
+
+              case 'u':
+                /* Restore the cursor position */
+                FLUSH_TEXT();
+                uv__tty_restore_state(handle, 0, error);
+                break;
+            }
+          }
+
+          /* Sequence ended - go back to normal state. */
+          ansi_parser_state = ANSI_NORMAL;
+          continue;
+
+        } else {
+          /* We don't support commands that use private mode characters or
+           * intermediaries. Ignore the rest of the sequence. */
+          ansi_parser_state = ANSI_IGNORE;
+          continue;
+        }
+
+      } else if (ansi_parser_state & ANSI_ST_CONTROL) {
+        /* Unsupported control code.
+         * Ignore everything until we see `BEL` or `ESC \`. */
+        if (ansi_parser_state & ANSI_IN_STRING) {
+          if (!(ansi_parser_state & ANSI_BACKSLASH_SEEN)) {
+            if (utf8_codepoint == '"') {
+              ansi_parser_state &= ~ANSI_IN_STRING;
+            } else if (utf8_codepoint == '\\') {
+              ansi_parser_state |= ANSI_BACKSLASH_SEEN;
+            }
+          } else {
+            ansi_parser_state &= ~ANSI_BACKSLASH_SEEN;
+          }
+        } else {
+          if (utf8_codepoint == '\007' || (utf8_codepoint == '\\' &&
+              (ansi_parser_state & ANSI_ESCAPE_SEEN))) {
+            /* End of sequence */
+            ansi_parser_state = ANSI_NORMAL;
+          } else if (utf8_codepoint == '\033') {
+            /* Escape character */
+            ansi_parser_state |= ANSI_ESCAPE_SEEN;
+          } else if (utf8_codepoint == '"') {
+             /* String starting */
+            ansi_parser_state |= ANSI_IN_STRING;
+            ansi_parser_state &= ~ANSI_ESCAPE_SEEN;
+            ansi_parser_state &= ~ANSI_BACKSLASH_SEEN;
+          } else {
+            ansi_parser_state &= ~ANSI_ESCAPE_SEEN;
+          }
+        }
+        continue;
+      } else {
+        /* Inconsistent state */
+        abort();
+      }
+
+      if (utf8_codepoint == 0x0a || utf8_codepoint == 0x0d) {
+        /* EOL conversion - emit \r\n when we see \n. */
+
+        if (utf8_codepoint == 0x0a && previous_eol != 0x0d) {
+          /* \n was not preceded by \r; print \r\n. */
+          ENSURE_BUFFER_SPACE(2);
+          utf16_buf[utf16_buf_used++] = L'\r';
+          utf16_buf[utf16_buf_used++] = L'\n';
+        } else if (utf8_codepoint == 0x0d && previous_eol == 0x0a) {
+          /* \n was followed by \r; do not print the \r, since the source was
+           * either \r\n\r (so the second \r is redundant) or was \n\r (so the
+           * \n was processed by the last case and an \r automatically
+           * inserted). */
+        } else {
+          /* \r without \n; print \r as-is. */
+          ENSURE_BUFFER_SPACE(1);
+          utf16_buf[utf16_buf_used++] = (WCHAR) utf8_codepoint;
+        }
+
+        previous_eol = (char) utf8_codepoint;
+
+      } else if (utf8_codepoint <= 0xffff) {
+        /* Encode character into utf-16 buffer. */
+        ENSURE_BUFFER_SPACE(1);
+        utf16_buf[utf16_buf_used++] = (WCHAR) utf8_codepoint;
+        previous_eol = 0;
+      } else {
+        ENSURE_BUFFER_SPACE(2);
+        utf8_codepoint -= 0x10000;
+        utf16_buf[utf16_buf_used++] = (WCHAR) (utf8_codepoint / 0x400 + 0xD800);
+        utf16_buf[utf16_buf_used++] = (WCHAR) (utf8_codepoint % 0x400 + 0xDC00);
+        previous_eol = 0;
+      }
+    }
+  }
+
+  /* Flush remaining characters */
+  FLUSH_TEXT();
+
+  /* Copy cached values back to struct. */
+  handle->tty.wr.utf8_bytes_left = utf8_bytes_left;
+  handle->tty.wr.utf8_codepoint = utf8_codepoint;
+  handle->tty.wr.previous_eol = previous_eol;
+  handle->tty.wr.ansi_parser_state = ansi_parser_state;
+
+  uv_sem_post(&uv_tty_output_lock);
+
+  if (*error == STATUS_SUCCESS) {
+    return 0;
+  } else {
+    return -1;
+  }
+
+#undef FLUSH_TEXT
+}
+
+
+int uv__tty_write(uv_loop_t* loop,
+                 uv_write_t* req,
+                 uv_tty_t* handle,
+                 const uv_buf_t bufs[],
+                 unsigned int nbufs,
+                 uv_write_cb cb) {
+  DWORD error;
+
+  UV_REQ_INIT(req, UV_WRITE);
+  req->handle = (uv_stream_t*) handle;
+  req->cb = cb;
+
+  handle->reqs_pending++;
+  handle->stream.conn.write_reqs_pending++;
+  REGISTER_HANDLE_REQ(loop, handle, req);
+
+  req->u.io.queued_bytes = 0;
+
+  if (!uv__tty_write_bufs(handle, bufs, nbufs, &error)) {
+    SET_REQ_SUCCESS(req);
+  } else {
+    SET_REQ_ERROR(req, error);
+  }
+
+  uv__insert_pending_req(loop, (uv_req_t*) req);
+
+  return 0;
+}
+
+
+int uv__tty_try_write(uv_tty_t* handle,
+                      const uv_buf_t bufs[],
+                      unsigned int nbufs) {
+  DWORD error;
+
+  if (handle->stream.conn.write_reqs_pending > 0)
+    return UV_EAGAIN;
+
+  if (uv__tty_write_bufs(handle, bufs, nbufs, &error))
+    return uv_translate_sys_error(error);
+
+  return uv__count_bufs(bufs, nbufs);
+}
+
+
+void uv__process_tty_write_req(uv_loop_t* loop, uv_tty_t* handle,
+  uv_write_t* req) {
+  int err;
+
+  handle->write_queue_size -= req->u.io.queued_bytes;
+  UNREGISTER_HANDLE_REQ(loop, handle, req);
+
+  if (req->cb) {
+    err = GET_REQ_ERROR(req);
+    req->cb(req, uv_translate_sys_error(err));
+  }
+
+
+  handle->stream.conn.write_reqs_pending--;
+  if (handle->stream.conn.write_reqs_pending == 0)
+    if (handle->flags & UV_HANDLE_SHUTTING)
+      uv__process_tty_shutdown_req(loop,
+                                   handle,
+                                   handle->stream.conn.shutdown_req);
+
+  DECREASE_PENDING_REQ_COUNT(handle);
+}
+
+
+void uv__tty_close(uv_tty_t* handle) {
+  assert(handle->u.fd == -1 || handle->u.fd > 2);
+  if (handle->flags & UV_HANDLE_READING)
+    uv__tty_read_stop(handle);
+
+  if (handle->u.fd == -1)
+    CloseHandle(handle->handle);
+  else
+    close(handle->u.fd);
+
+  handle->u.fd = -1;
+  handle->handle = INVALID_HANDLE_VALUE;
+  handle->flags &= ~(UV_HANDLE_READABLE | UV_HANDLE_WRITABLE);
+  uv__handle_closing(handle);
+
+  if (handle->reqs_pending == 0)
+    uv__want_endgame(handle->loop, (uv_handle_t*) handle);
+}
+
+
+void uv__process_tty_shutdown_req(uv_loop_t* loop, uv_tty_t* stream, uv_shutdown_t* req) {
+  assert(stream->stream.conn.write_reqs_pending == 0);
+  assert(req);
+
+  stream->stream.conn.shutdown_req = NULL;
+  stream->flags &= ~UV_HANDLE_SHUTTING;
+  UNREGISTER_HANDLE_REQ(loop, stream, req);
+
+  /* TTY shutdown is really just a no-op */
+  if (req->cb) {
+    if (stream->flags & UV_HANDLE_CLOSING) {
+      req->cb(req, UV_ECANCELED);
+    } else {
+      req->cb(req, 0);
+    }
+  }
+
+  DECREASE_PENDING_REQ_COUNT(stream);
+}
+
+
+void uv__tty_endgame(uv_loop_t* loop, uv_tty_t* handle) {
+  assert(handle->flags & UV_HANDLE_CLOSING);
+  assert(handle->reqs_pending == 0);
+
+  /* The wait handle used for raw reading should be unregistered when the
+   * wait callback runs. */
+  assert(!(handle->flags & UV_HANDLE_TTY_READABLE) ||
+         handle->tty.rd.read_raw_wait == NULL);
+
+  assert(!(handle->flags & UV_HANDLE_CLOSED));
+  uv__handle_close(handle);
+}
+
+
+/*
+ * uv__process_tty_accept_req() is a stub to keep DELEGATE_STREAM_REQ working
+ * TODO: find a way to remove it
+ */
+void uv__process_tty_accept_req(uv_loop_t* loop, uv_tty_t* handle,
+    uv_req_t* raw_req) {
+  abort();
+}
+
+
+/*
+ * uv__process_tty_connect_req() is a stub to keep DELEGATE_STREAM_REQ working
+ * TODO: find a way to remove it
+ */
+void uv__process_tty_connect_req(uv_loop_t* loop, uv_tty_t* handle,
+    uv_connect_t* req) {
+  abort();
+}
+
+
+int uv_tty_reset_mode(void) {
+  /* Not necessary to do anything. */
+  return 0;
+}
+
+/* Determine whether or not this version of windows supports
+ * proper ANSI color codes. Should be supported as of windows
+ * 10 version 1511, build number 10.0.10586.
+ */
+static void uv__determine_vterm_state(HANDLE handle) {
+  DWORD dwMode = 0;
+
+  uv__need_check_vterm_state = FALSE;
+  if (!GetConsoleMode(handle, &dwMode)) {
+    return;
+  }
+
+  dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
+  if (!SetConsoleMode(handle, dwMode)) {
+    return;
+  }
+
+  uv__vterm_state = UV_TTY_SUPPORTED;
+}
+
+static DWORD WINAPI uv__tty_console_resize_message_loop_thread(void* param) {
+  NTSTATUS status;
+  ULONG_PTR conhost_pid;
+  MSG msg;
+
+  if (pSetWinEventHook == NULL || pNtQueryInformationProcess == NULL)
+    return 0;
+
+  status = pNtQueryInformationProcess(GetCurrentProcess(),
+                                      ProcessConsoleHostProcess,
+                                      &conhost_pid,
+                                      sizeof(conhost_pid),
+                                      NULL);
+
+  if (!NT_SUCCESS(status)) {
+    /* We couldn't retrieve our console host process, probably because this
+     * is a 32-bit process running on 64-bit Windows. Fall back to receiving
+     * console events from the input stream only. */
+    return 0;
+  }
+
+  /* Ensure the PID is a multiple of 4, which is required by SetWinEventHook */
+  conhost_pid &= ~(ULONG_PTR)0x3;
+
+  uv__tty_console_resized = CreateEvent(NULL, TRUE, FALSE, NULL);
+  if (uv__tty_console_resized == NULL)
+    return 0;
+  if (QueueUserWorkItem(uv__tty_console_resize_watcher_thread,
+                        NULL,
+                        WT_EXECUTELONGFUNCTION) == 0)
+    return 0;
+
+  if (!pSetWinEventHook(EVENT_CONSOLE_LAYOUT,
+                        EVENT_CONSOLE_LAYOUT,
+                        NULL,
+                        uv__tty_console_resize_event,
+                        (DWORD)conhost_pid,
+                        0,
+                        WINEVENT_OUTOFCONTEXT))
+    return 0;
+
+  while (GetMessage(&msg, NULL, 0, 0)) {
+    TranslateMessage(&msg);
+    DispatchMessage(&msg);
+  }
+  return 0;
+}
+
+static void CALLBACK uv__tty_console_resize_event(HWINEVENTHOOK hWinEventHook,
+                                                  DWORD event,
+                                                  HWND hwnd,
+                                                  LONG idObject,
+                                                  LONG idChild,
+                                                  DWORD dwEventThread,
+                                                  DWORD dwmsEventTime) {
+  SetEvent(uv__tty_console_resized);
+}
+
+static DWORD WINAPI uv__tty_console_resize_watcher_thread(void* param) {
+  for (;;) {
+    /* Make sure to not overwhelm the system with resize events */
+    Sleep(33);
+    WaitForSingleObject(uv__tty_console_resized, INFINITE);
+    uv__tty_console_signal_resize();
+    ResetEvent(uv__tty_console_resized);
+  }
+  return 0;
+}
+
+static void uv__tty_console_signal_resize(void) {
+  CONSOLE_SCREEN_BUFFER_INFO sb_info;
+  int width, height;
+
+  if (!GetConsoleScreenBufferInfo(uv__tty_console_handle, &sb_info))
+    return;
+
+  width = sb_info.dwSize.X;
+  height = sb_info.srWindow.Bottom - sb_info.srWindow.Top + 1;
+
+  uv_mutex_lock(&uv__tty_console_resize_mutex);
+  assert(uv__tty_console_width != -1 && uv__tty_console_height != -1);
+  if (width != uv__tty_console_width || height != uv__tty_console_height) {
+    uv__tty_console_width = width;
+    uv__tty_console_height = height;
+    uv_mutex_unlock(&uv__tty_console_resize_mutex);
+    uv__signal_dispatch(SIGWINCH);
+  } else {
+    uv_mutex_unlock(&uv__tty_console_resize_mutex);
+  }
+}
+
+void uv_tty_set_vterm_state(uv_tty_vtermstate_t state) {
+  uv_sem_wait(&uv_tty_output_lock);
+  uv__need_check_vterm_state = FALSE;
+  uv__vterm_state = state;
+  uv_sem_post(&uv_tty_output_lock);
+}
+
+int uv_tty_get_vterm_state(uv_tty_vtermstate_t* state) {
+  uv_sem_wait(&uv_tty_output_lock);
+  *state = uv__vterm_state;
+  uv_sem_post(&uv_tty_output_lock);
+  return 0;
+}
diff --git a/wpinet/src/main/native/thirdparty/libuv/src/win/udp.cpp b/wpinet/src/main/native/thirdparty/libuv/src/win/udp.cpp
new file mode 100644
index 0000000..eaebc1e
--- /dev/null
+++ b/wpinet/src/main/native/thirdparty/libuv/src/win/udp.cpp
@@ -0,0 +1,1182 @@
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include <assert.h>
+#include <stdlib.h>
+
+#include "uv.h"
+#include "internal.h"
+#include "handle-inl.h"
+#include "stream-inl.h"
+#include "req-inl.h"
+
+
+/*
+ * Threshold of active udp streams for which to preallocate udp read buffers.
+ */
+const unsigned int uv_active_udp_streams_threshold = 0;
+
+/* A zero-size buffer for use by uv_udp_read */
+static char uv_zero_[] = "";
+int uv_udp_getpeername(const uv_udp_t* handle,
+                       struct sockaddr* name,
+                       int* namelen) {
+
+  return uv__getsockpeername((const uv_handle_t*) handle,
+                             getpeername,
+                             name,
+                             namelen,
+                             0);
+}
+
+
+int uv_udp_getsockname(const uv_udp_t* handle,
+                       struct sockaddr* name,
+                       int* namelen) {
+
+  return uv__getsockpeername((const uv_handle_t*) handle,
+                             getsockname,
+                             name,
+                             namelen,
+                             0);
+}
+
+
+static int uv__udp_set_socket(uv_loop_t* loop, uv_udp_t* handle, SOCKET socket,
+    int family) {
+  DWORD yes = 1;
+  WSAPROTOCOL_INFOW info;
+  int opt_len;
+
+  if (handle->socket != INVALID_SOCKET)
+    return UV_EBUSY;
+
+  /* Set the socket to nonblocking mode */
+  if (ioctlsocket(socket, FIONBIO, &yes) == SOCKET_ERROR) {
+    return WSAGetLastError();
+  }
+
+  /* Make the socket non-inheritable */
+  if (!SetHandleInformation((HANDLE)socket, HANDLE_FLAG_INHERIT, 0)) {
+    return GetLastError();
+  }
+
+  /* Associate it with the I/O completion port. Use uv_handle_t pointer as
+   * completion key. */
+  if (CreateIoCompletionPort((HANDLE)socket,
+                             loop->iocp,
+                             (ULONG_PTR)socket,
+                             0) == NULL) {
+    return GetLastError();
+  }
+
+  /* All known Windows that support SetFileCompletionNotificationModes have a
+   * bug that makes it impossible to use this function in conjunction with
+   * datagram sockets. We can work around that but only if the user is using
+   * the default UDP driver (AFD) and has no other. LSPs stacked on top. Here
+   * we check whether that is the case. */
+  opt_len = (int) sizeof info;
+  if (getsockopt(
+          socket, SOL_SOCKET, SO_PROTOCOL_INFOW, (char*) &info, &opt_len) ==
+      SOCKET_ERROR) {
+    return GetLastError();
+  }
+
+  if (info.ProtocolChain.ChainLen == 1) {
+    if (SetFileCompletionNotificationModes(
+            (HANDLE) socket,
+            FILE_SKIP_SET_EVENT_ON_HANDLE |
+                FILE_SKIP_COMPLETION_PORT_ON_SUCCESS)) {
+      handle->flags |= UV_HANDLE_SYNC_BYPASS_IOCP;
+      handle->func_wsarecv = uv__wsarecv_workaround;
+      handle->func_wsarecvfrom = uv__wsarecvfrom_workaround;
+    } else if (GetLastError() != ERROR_INVALID_FUNCTION) {
+      return GetLastError();
+    }
+  }
+
+  handle->socket = socket;
+
+  if (family == AF_INET6) {
+    handle->flags |= UV_HANDLE_IPV6;
+  } else {
+    assert(!(handle->flags & UV_HANDLE_IPV6));
+  }
+
+  return 0;
+}
+
+
+int uv__udp_init_ex(uv_loop_t* loop,
+                    uv_udp_t* handle,
+                    unsigned flags,
+                    int domain) {
+  uv__handle_init(loop, (uv_handle_t*) handle, UV_UDP);
+  handle->socket = INVALID_SOCKET;
+  handle->reqs_pending = 0;
+  handle->activecnt = 0;
+  handle->func_wsarecv = WSARecv;
+  handle->func_wsarecvfrom = WSARecvFrom;
+  handle->send_queue_size = 0;
+  handle->send_queue_count = 0;
+  UV_REQ_INIT(&handle->recv_req, UV_UDP_RECV);
+  handle->recv_req.data = handle;
+
+  /* If anything fails beyond this point we need to remove the handle from
+   * the handle queue, since it was added by uv__handle_init.
+   */
+
+  if (domain != AF_UNSPEC) {
+    SOCKET sock;
+    DWORD err;
+
+    sock = socket(domain, SOCK_DGRAM, 0);
+    if (sock == INVALID_SOCKET) {
+      err = WSAGetLastError();
+      QUEUE_REMOVE(&handle->handle_queue);
+      return uv_translate_sys_error(err);
+    }
+
+    err = uv__udp_set_socket(handle->loop, handle, sock, domain);
+    if (err) {
+      closesocket(sock);
+      QUEUE_REMOVE(&handle->handle_queue);
+      return uv_translate_sys_error(err);
+    }
+  }
+
+  return 0;
+}
+
+
+void uv__udp_close(uv_loop_t* loop, uv_udp_t* handle) {
+  uv_udp_recv_stop(handle);
+  closesocket(handle->socket);
+  handle->socket = INVALID_SOCKET;
+
+  uv__handle_closing(handle);
+
+  if (handle->reqs_pending == 0) {
+    uv__want_endgame(loop, (uv_handle_t*) handle);
+  }
+}
+
+
+void uv__udp_endgame(uv_loop_t* loop, uv_udp_t* handle) {
+  if (handle->flags & UV_HANDLE_CLOSING &&
+      handle->reqs_pending == 0) {
+    assert(!(handle->flags & UV_HANDLE_CLOSED));
+    uv__handle_close(handle);
+  }
+}
+
+
+int uv_udp_using_recvmmsg(const uv_udp_t* handle) {
+  return 0;
+}
+
+
+static int uv__udp_maybe_bind(uv_udp_t* handle,
+                              const struct sockaddr* addr,
+                              unsigned int addrlen,
+                              unsigned int flags) {
+  int r;
+  int err;
+  DWORD no = 0;
+
+  if (handle->flags & UV_HANDLE_BOUND)
+    return 0;
+
+  if ((flags & UV_UDP_IPV6ONLY) && addr->sa_family != AF_INET6) {
+    /* UV_UDP_IPV6ONLY is supported only for IPV6 sockets */
+    return ERROR_INVALID_PARAMETER;
+  }
+
+  if (handle->socket == INVALID_SOCKET) {
+    SOCKET sock = socket(addr->sa_family, SOCK_DGRAM, 0);
+    if (sock == INVALID_SOCKET) {
+      return WSAGetLastError();
+    }
+
+    err = uv__udp_set_socket(handle->loop, handle, sock, addr->sa_family);
+    if (err) {
+      closesocket(sock);
+      return err;
+    }
+  }
+
+  if (flags & UV_UDP_REUSEADDR) {
+    DWORD yes = 1;
+    /* Set SO_REUSEADDR on the socket. */
+    if (setsockopt(handle->socket,
+                   SOL_SOCKET,
+                   SO_REUSEADDR,
+                   (char*) &yes,
+                   sizeof yes) == SOCKET_ERROR) {
+      err = WSAGetLastError();
+      return err;
+    }
+  }
+
+  if (addr->sa_family == AF_INET6)
+    handle->flags |= UV_HANDLE_IPV6;
+
+  if (addr->sa_family == AF_INET6 && !(flags & UV_UDP_IPV6ONLY)) {
+    /* On windows IPV6ONLY is on by default. If the user doesn't specify it
+     * libuv turns it off. */
+
+    /* TODO: how to handle errors? This may fail if there is no ipv4 stack
+     * available, or when run on XP/2003 which have no support for dualstack
+     * sockets. For now we're silently ignoring the error. */
+    setsockopt(handle->socket,
+               IPPROTO_IPV6,
+               IPV6_V6ONLY,
+               (char*) &no,
+               sizeof no);
+  }
+
+  r = bind(handle->socket, addr, addrlen);
+  if (r == SOCKET_ERROR) {
+    return WSAGetLastError();
+  }
+
+  handle->flags |= UV_HANDLE_BOUND;
+
+  return 0;
+}
+
+
+static void uv__udp_queue_recv(uv_loop_t* loop, uv_udp_t* handle) {
+  uv_req_t* req;
+  uv_buf_t buf;
+  DWORD bytes, flags;
+  int result;
+
+  assert(handle->flags & UV_HANDLE_READING);
+  assert(!(handle->flags & UV_HANDLE_READ_PENDING));
+
+  req = &handle->recv_req;
+  memset(&req->u.io.overlapped, 0, sizeof(req->u.io.overlapped));
+
+  /*
+   * Preallocate a read buffer if the number of active streams is below
+   * the threshold.
+  */
+  if (loop->active_udp_streams < uv_active_udp_streams_threshold) {
+    handle->flags &= ~UV_HANDLE_ZERO_READ;
+
+    handle->recv_buffer = uv_buf_init(NULL, 0);
+    handle->alloc_cb((uv_handle_t*) handle, UV__UDP_DGRAM_MAXSIZE, &handle->recv_buffer);
+    if (handle->recv_buffer.base == NULL || handle->recv_buffer.len == 0) {
+      handle->recv_cb(handle, UV_ENOBUFS, &handle->recv_buffer, NULL, 0);
+      return;
+    }
+    assert(handle->recv_buffer.base != NULL);
+
+    buf = handle->recv_buffer;
+    memset(&handle->recv_from, 0, sizeof handle->recv_from);
+    handle->recv_from_len = sizeof handle->recv_from;
+    flags = 0;
+
+    result = handle->func_wsarecvfrom(handle->socket,
+                                      (WSABUF*) &buf,
+                                      1,
+                                      &bytes,
+                                      &flags,
+                                      (struct sockaddr*) &handle->recv_from,
+                                      &handle->recv_from_len,
+                                      &req->u.io.overlapped,
+                                      NULL);
+
+    if (UV_SUCCEEDED_WITHOUT_IOCP(result == 0)) {
+      /* Process the req without IOCP. */
+      handle->flags |= UV_HANDLE_READ_PENDING;
+      req->u.io.overlapped.InternalHigh = bytes;
+      handle->reqs_pending++;
+      uv__insert_pending_req(loop, req);
+    } else if (UV_SUCCEEDED_WITH_IOCP(result == 0)) {
+      /* The req will be processed with IOCP. */
+      handle->flags |= UV_HANDLE_READ_PENDING;
+      handle->reqs_pending++;
+    } else {
+      /* Make this req pending reporting an error. */
+      SET_REQ_ERROR(req, WSAGetLastError());
+      uv__insert_pending_req(loop, req);
+      handle->reqs_pending++;
+    }
+
+  } else {
+    handle->flags |= UV_HANDLE_ZERO_READ;
+
+    buf.base = (char*) uv_zero_;
+    buf.len = 0;
+    flags = MSG_PEEK;
+
+    result = handle->func_wsarecv(handle->socket,
+                                  (WSABUF*) &buf,
+                                  1,
+                                  &bytes,
+                                  &flags,
+                                  &req->u.io.overlapped,
+                                  NULL);
+
+    if (UV_SUCCEEDED_WITHOUT_IOCP(result == 0)) {
+      /* Process the req without IOCP. */
+      handle->flags |= UV_HANDLE_READ_PENDING;
+      req->u.io.overlapped.InternalHigh = bytes;
+      handle->reqs_pending++;
+      uv__insert_pending_req(loop, req);
+    } else if (UV_SUCCEEDED_WITH_IOCP(result == 0)) {
+      /* The req will be processed with IOCP. */
+      handle->flags |= UV_HANDLE_READ_PENDING;
+      handle->reqs_pending++;
+    } else {
+      /* Make this req pending reporting an error. */
+      SET_REQ_ERROR(req, WSAGetLastError());
+      uv__insert_pending_req(loop, req);
+      handle->reqs_pending++;
+    }
+  }
+}
+
+
+int uv__udp_recv_start(uv_udp_t* handle, uv_alloc_cb alloc_cb,
+    uv_udp_recv_cb recv_cb) {
+  uv_loop_t* loop = handle->loop;
+  int err;
+
+  if (handle->flags & UV_HANDLE_READING) {
+    return UV_EALREADY;
+  }
+
+  err = uv__udp_maybe_bind(handle,
+                           (const struct sockaddr*) &uv_addr_ip4_any_,
+                           sizeof(uv_addr_ip4_any_),
+                           0);
+  if (err)
+    return uv_translate_sys_error(err);
+
+  handle->flags |= UV_HANDLE_READING;
+  INCREASE_ACTIVE_COUNT(loop, handle);
+  loop->active_udp_streams++;
+
+  handle->recv_cb = recv_cb;
+  handle->alloc_cb = alloc_cb;
+
+  /* If reading was stopped and then started again, there could still be a recv
+   * request pending. */
+  if (!(handle->flags & UV_HANDLE_READ_PENDING))
+    uv__udp_queue_recv(loop, handle);
+
+  return 0;
+}
+
+
+int uv__udp_recv_stop(uv_udp_t* handle) {
+  if (handle->flags & UV_HANDLE_READING) {
+    handle->flags &= ~UV_HANDLE_READING;
+    handle->loop->active_udp_streams--;
+    DECREASE_ACTIVE_COUNT(loop, handle);
+  }
+
+  return 0;
+}
+
+
+static int uv__send(uv_udp_send_t* req,
+                    uv_udp_t* handle,
+                    const uv_buf_t bufs[],
+                    unsigned int nbufs,
+                    const struct sockaddr* addr,
+                    unsigned int addrlen,
+                    uv_udp_send_cb cb) {
+  uv_loop_t* loop = handle->loop;
+  DWORD result, bytes;
+
+  UV_REQ_INIT(req, UV_UDP_SEND);
+  req->handle = handle;
+  req->cb = cb;
+  memset(&req->u.io.overlapped, 0, sizeof(req->u.io.overlapped));
+
+  result = WSASendTo(handle->socket,
+                     (WSABUF*)bufs,
+                     nbufs,
+                     &bytes,
+                     0,
+                     addr,
+                     addrlen,
+                     &req->u.io.overlapped,
+                     NULL);
+
+  if (UV_SUCCEEDED_WITHOUT_IOCP(result == 0)) {
+    /* Request completed immediately. */
+    req->u.io.queued_bytes = 0;
+    handle->reqs_pending++;
+    handle->send_queue_size += req->u.io.queued_bytes;
+    handle->send_queue_count++;
+    REGISTER_HANDLE_REQ(loop, handle, req);
+    uv__insert_pending_req(loop, (uv_req_t*)req);
+  } else if (UV_SUCCEEDED_WITH_IOCP(result == 0)) {
+    /* Request queued by the kernel. */
+    req->u.io.queued_bytes = uv__count_bufs(bufs, nbufs);
+    handle->reqs_pending++;
+    handle->send_queue_size += req->u.io.queued_bytes;
+    handle->send_queue_count++;
+    REGISTER_HANDLE_REQ(loop, handle, req);
+  } else {
+    /* Send failed due to an error. */
+    return WSAGetLastError();
+  }
+
+  return 0;
+}
+
+
+void uv__process_udp_recv_req(uv_loop_t* loop, uv_udp_t* handle,
+    uv_req_t* req) {
+  uv_buf_t buf;
+  int partial;
+
+  assert(handle->type == UV_UDP);
+
+  handle->flags &= ~UV_HANDLE_READ_PENDING;
+
+  if (!REQ_SUCCESS(req)) {
+    DWORD err = GET_REQ_SOCK_ERROR(req);
+    if (err == WSAEMSGSIZE) {
+      /* Not a real error, it just indicates that the received packet was
+       * bigger than the receive buffer. */
+    } else if (err == WSAECONNRESET || err == WSAENETRESET) {
+      /* A previous sendto operation failed; ignore this error. If zero-reading
+       * we need to call WSARecv/WSARecvFrom _without_ the. MSG_PEEK flag to
+       * clear out the error queue. For nonzero reads, immediately queue a new
+       * receive. */
+      if (!(handle->flags & UV_HANDLE_ZERO_READ)) {
+        goto done;
+      }
+    } else {
+      /* A real error occurred. Report the error to the user only if we're
+       * currently reading. */
+      if (handle->flags & UV_HANDLE_READING) {
+        uv_udp_recv_stop(handle);
+        buf = (handle->flags & UV_HANDLE_ZERO_READ) ?
+              uv_buf_init(NULL, 0) : handle->recv_buffer;
+        handle->recv_cb(handle, uv_translate_sys_error(err), &buf, NULL, 0);
+      }
+      goto done;
+    }
+  }
+
+  if (!(handle->flags & UV_HANDLE_ZERO_READ)) {
+    /* Successful read */
+    partial = !REQ_SUCCESS(req);
+    handle->recv_cb(handle,
+                    req->u.io.overlapped.InternalHigh,
+                    &handle->recv_buffer,
+                    (const struct sockaddr*) &handle->recv_from,
+                    partial ? UV_UDP_PARTIAL : 0);
+  } else if (handle->flags & UV_HANDLE_READING) {
+    DWORD bytes, err, flags;
+    struct sockaddr_storage from;
+    int from_len;
+
+    /* Do a nonblocking receive.
+     * TODO: try to read multiple datagrams at once. FIONREAD maybe? */
+    buf = uv_buf_init(NULL, 0);
+    handle->alloc_cb((uv_handle_t*) handle, UV__UDP_DGRAM_MAXSIZE, &buf);
+    if (buf.base == NULL || buf.len == 0) {
+      handle->recv_cb(handle, UV_ENOBUFS, &buf, NULL, 0);
+      goto done;
+    }
+    assert(buf.base != NULL);
+
+    memset(&from, 0, sizeof from);
+    from_len = sizeof from;
+
+    flags = 0;
+
+    if (WSARecvFrom(handle->socket,
+                    (WSABUF*)&buf,
+                    1,
+                    &bytes,
+                    &flags,
+                    (struct sockaddr*) &from,
+                    &from_len,
+                    NULL,
+                    NULL) != SOCKET_ERROR) {
+
+      /* Message received */
+      handle->recv_cb(handle, bytes, &buf, (const struct sockaddr*) &from, 0);
+    } else {
+      err = WSAGetLastError();
+      if (err == WSAEMSGSIZE) {
+        /* Message truncated */
+        handle->recv_cb(handle,
+                        bytes,
+                        &buf,
+                        (const struct sockaddr*) &from,
+                        UV_UDP_PARTIAL);
+      } else if (err == WSAEWOULDBLOCK) {
+        /* Kernel buffer empty */
+        handle->recv_cb(handle, 0, &buf, NULL, 0);
+      } else if (err == WSAECONNRESET || err == WSAENETRESET) {
+        /* WSAECONNRESET/WSANETRESET is ignored because this just indicates
+         * that a previous sendto operation failed.
+         */
+        handle->recv_cb(handle, 0, &buf, NULL, 0);
+      } else {
+        /* Any other error that we want to report back to the user. */
+        uv_udp_recv_stop(handle);
+        handle->recv_cb(handle, uv_translate_sys_error(err), &buf, NULL, 0);
+      }
+    }
+  }
+
+done:
+  /* Post another read if still reading and not closing. */
+  if ((handle->flags & UV_HANDLE_READING) &&
+      !(handle->flags & UV_HANDLE_READ_PENDING)) {
+    uv__udp_queue_recv(loop, handle);
+  }
+
+  DECREASE_PENDING_REQ_COUNT(handle);
+}
+
+
+void uv__process_udp_send_req(uv_loop_t* loop, uv_udp_t* handle,
+    uv_udp_send_t* req) {
+  int err;
+
+  assert(handle->type == UV_UDP);
+
+  assert(handle->send_queue_size >= req->u.io.queued_bytes);
+  assert(handle->send_queue_count >= 1);
+  handle->send_queue_size -= req->u.io.queued_bytes;
+  handle->send_queue_count--;
+
+  UNREGISTER_HANDLE_REQ(loop, handle, req);
+
+  if (req->cb) {
+    err = 0;
+    if (!REQ_SUCCESS(req)) {
+      err = GET_REQ_SOCK_ERROR(req);
+    }
+    req->cb(req, uv_translate_sys_error(err));
+  }
+
+  DECREASE_PENDING_REQ_COUNT(handle);
+}
+
+
+static int uv__udp_set_membership4(uv_udp_t* handle,
+                                   const struct sockaddr_in* multicast_addr,
+                                   const char* interface_addr,
+                                   uv_membership membership) {
+  int err;
+  int optname;
+  struct ip_mreq mreq;
+
+  if (handle->flags & UV_HANDLE_IPV6)
+    return UV_EINVAL;
+
+  /* If the socket is unbound, bind to inaddr_any. */
+  err = uv__udp_maybe_bind(handle,
+                           (const struct sockaddr*) &uv_addr_ip4_any_,
+                           sizeof(uv_addr_ip4_any_),
+                           UV_UDP_REUSEADDR);
+  if (err)
+    return uv_translate_sys_error(err);
+
+  memset(&mreq, 0, sizeof mreq);
+
+  if (interface_addr) {
+    err = uv_inet_pton(AF_INET, interface_addr, &mreq.imr_interface.s_addr);
+    if (err)
+      return err;
+  } else {
+    mreq.imr_interface.s_addr = htonl(INADDR_ANY);
+  }
+
+  mreq.imr_multiaddr.s_addr = multicast_addr->sin_addr.s_addr;
+
+  switch (membership) {
+    case UV_JOIN_GROUP:
+      optname = IP_ADD_MEMBERSHIP;
+      break;
+    case UV_LEAVE_GROUP:
+      optname = IP_DROP_MEMBERSHIP;
+      break;
+    default:
+      return UV_EINVAL;
+  }
+
+  if (setsockopt(handle->socket,
+                 IPPROTO_IP,
+                 optname,
+                 (char*) &mreq,
+                 sizeof mreq) == SOCKET_ERROR) {
+    return uv_translate_sys_error(WSAGetLastError());
+  }
+
+  return 0;
+}
+
+
+int uv__udp_set_membership6(uv_udp_t* handle,
+                            const struct sockaddr_in6* multicast_addr,
+                            const char* interface_addr,
+                            uv_membership membership) {
+  int optname;
+  int err;
+  struct ipv6_mreq mreq;
+  struct sockaddr_in6 addr6;
+
+  if ((handle->flags & UV_HANDLE_BOUND) && !(handle->flags & UV_HANDLE_IPV6))
+    return UV_EINVAL;
+
+  err = uv__udp_maybe_bind(handle,
+                           (const struct sockaddr*) &uv_addr_ip6_any_,
+                           sizeof(uv_addr_ip6_any_),
+                           UV_UDP_REUSEADDR);
+
+  if (err)
+    return uv_translate_sys_error(err);
+
+  memset(&mreq, 0, sizeof(mreq));
+
+  if (interface_addr) {
+    if (uv_ip6_addr(interface_addr, 0, &addr6))
+      return UV_EINVAL;
+    mreq.ipv6mr_interface = addr6.sin6_scope_id;
+  } else {
+    mreq.ipv6mr_interface = 0;
+  }
+
+  mreq.ipv6mr_multiaddr = multicast_addr->sin6_addr;
+
+  switch (membership) {
+  case UV_JOIN_GROUP:
+    optname = IPV6_ADD_MEMBERSHIP;
+    break;
+  case UV_LEAVE_GROUP:
+    optname = IPV6_DROP_MEMBERSHIP;
+    break;
+  default:
+    return UV_EINVAL;
+  }
+
+  if (setsockopt(handle->socket,
+                 IPPROTO_IPV6,
+                 optname,
+                 (char*) &mreq,
+                 sizeof mreq) == SOCKET_ERROR) {
+    return uv_translate_sys_error(WSAGetLastError());
+  }
+
+  return 0;
+}
+
+
+static int uv__udp_set_source_membership4(uv_udp_t* handle,
+                                          const struct sockaddr_in* multicast_addr,
+                                          const char* interface_addr,
+                                          const struct sockaddr_in* source_addr,
+                                          uv_membership membership) {
+  struct ip_mreq_source mreq;
+  int optname;
+  int err;
+
+  if (handle->flags & UV_HANDLE_IPV6)
+    return UV_EINVAL;
+
+  /* If the socket is unbound, bind to inaddr_any. */
+  err = uv__udp_maybe_bind(handle,
+                           (const struct sockaddr*) &uv_addr_ip4_any_,
+                           sizeof(uv_addr_ip4_any_),
+                           UV_UDP_REUSEADDR);
+  if (err)
+    return uv_translate_sys_error(err);
+
+  memset(&mreq, 0, sizeof(mreq));
+
+  if (interface_addr != NULL) {
+    err = uv_inet_pton(AF_INET, interface_addr, &mreq.imr_interface.s_addr);
+    if (err)
+      return err;
+  } else {
+    mreq.imr_interface.s_addr = htonl(INADDR_ANY);
+  }
+
+  mreq.imr_multiaddr.s_addr = multicast_addr->sin_addr.s_addr;
+  mreq.imr_sourceaddr.s_addr = source_addr->sin_addr.s_addr;
+
+  if (membership == UV_JOIN_GROUP)
+    optname = IP_ADD_SOURCE_MEMBERSHIP;
+  else if (membership == UV_LEAVE_GROUP)
+    optname = IP_DROP_SOURCE_MEMBERSHIP;
+  else
+    return UV_EINVAL;
+
+  if (setsockopt(handle->socket,
+                 IPPROTO_IP,
+                 optname,
+                 (char*) &mreq,
+                 sizeof(mreq)) == SOCKET_ERROR) {
+    return uv_translate_sys_error(WSAGetLastError());
+  }
+
+  return 0;
+}
+
+
+int uv__udp_set_source_membership6(uv_udp_t* handle,
+                                   const struct sockaddr_in6* multicast_addr,
+                                   const char* interface_addr,
+                                   const struct sockaddr_in6* source_addr,
+                                   uv_membership membership) {
+  struct group_source_req mreq;
+  struct sockaddr_in6 addr6;
+  int optname;
+  int err;
+
+  STATIC_ASSERT(sizeof(mreq.gsr_group) >= sizeof(*multicast_addr));
+  STATIC_ASSERT(sizeof(mreq.gsr_source) >= sizeof(*source_addr));
+
+  if ((handle->flags & UV_HANDLE_BOUND) && !(handle->flags & UV_HANDLE_IPV6))
+    return UV_EINVAL;
+
+  err = uv__udp_maybe_bind(handle,
+                           (const struct sockaddr*) &uv_addr_ip6_any_,
+                           sizeof(uv_addr_ip6_any_),
+                           UV_UDP_REUSEADDR);
+
+  if (err)
+    return uv_translate_sys_error(err);
+
+  memset(&mreq, 0, sizeof(mreq));
+
+  if (interface_addr != NULL) {
+    err = uv_ip6_addr(interface_addr, 0, &addr6);
+    if (err)
+      return err;
+    mreq.gsr_interface = addr6.sin6_scope_id;
+  } else {
+    mreq.gsr_interface = 0;
+  }
+
+  memcpy(&mreq.gsr_group, multicast_addr, sizeof(*multicast_addr));
+  memcpy(&mreq.gsr_source, source_addr, sizeof(*source_addr));
+
+  if (membership == UV_JOIN_GROUP)
+    optname = MCAST_JOIN_SOURCE_GROUP;
+  else if (membership == UV_LEAVE_GROUP)
+    optname = MCAST_LEAVE_SOURCE_GROUP;
+  else
+    return UV_EINVAL;
+
+  if (setsockopt(handle->socket,
+                 IPPROTO_IPV6,
+                 optname,
+                 (char*) &mreq,
+                 sizeof(mreq)) == SOCKET_ERROR) {
+    return uv_translate_sys_error(WSAGetLastError());
+  }
+
+  return 0;
+}
+
+
+int uv_udp_set_membership(uv_udp_t* handle,
+                          const char* multicast_addr,
+                          const char* interface_addr,
+                          uv_membership membership) {
+  struct sockaddr_in addr4;
+  struct sockaddr_in6 addr6;
+
+  if (uv_ip4_addr(multicast_addr, 0, &addr4) == 0)
+    return uv__udp_set_membership4(handle, &addr4, interface_addr, membership);
+  else if (uv_ip6_addr(multicast_addr, 0, &addr6) == 0)
+    return uv__udp_set_membership6(handle, &addr6, interface_addr, membership);
+  else
+    return UV_EINVAL;
+}
+
+
+int uv_udp_set_source_membership(uv_udp_t* handle,
+                                 const char* multicast_addr,
+                                 const char* interface_addr,
+                                 const char* source_addr,
+                                 uv_membership membership) {
+  int err;
+  struct sockaddr_storage mcast_addr;
+  struct sockaddr_in* mcast_addr4;
+  struct sockaddr_in6* mcast_addr6;
+  struct sockaddr_storage src_addr;
+  struct sockaddr_in* src_addr4;
+  struct sockaddr_in6* src_addr6;
+
+  mcast_addr4 = (struct sockaddr_in*)&mcast_addr;
+  mcast_addr6 = (struct sockaddr_in6*)&mcast_addr;
+  src_addr4 = (struct sockaddr_in*)&src_addr;
+  src_addr6 = (struct sockaddr_in6*)&src_addr;
+
+  err = uv_ip4_addr(multicast_addr, 0, mcast_addr4);
+  if (err) {
+    err = uv_ip6_addr(multicast_addr, 0, mcast_addr6);
+    if (err)
+      return err;
+    err = uv_ip6_addr(source_addr, 0, src_addr6);
+    if (err)
+      return err;
+    return uv__udp_set_source_membership6(handle,
+                                          mcast_addr6,
+                                          interface_addr,
+                                          src_addr6,
+                                          membership);
+  }
+  
+  err = uv_ip4_addr(source_addr, 0, src_addr4);
+  if (err)
+    return err;
+  return uv__udp_set_source_membership4(handle,
+                                        mcast_addr4,
+                                        interface_addr,
+                                        src_addr4,
+                                        membership);
+}
+
+
+int uv_udp_set_multicast_interface(uv_udp_t* handle, const char* interface_addr) {
+  struct sockaddr_storage addr_st;
+  struct sockaddr_in* addr4;
+  struct sockaddr_in6* addr6;
+
+  addr4 = (struct sockaddr_in*) &addr_st;
+  addr6 = (struct sockaddr_in6*) &addr_st;
+
+  if (!interface_addr) {
+    memset(&addr_st, 0, sizeof addr_st);
+    if (handle->flags & UV_HANDLE_IPV6) {
+      addr_st.ss_family = AF_INET6;
+      addr6->sin6_scope_id = 0;
+    } else {
+      addr_st.ss_family = AF_INET;
+      addr4->sin_addr.s_addr = htonl(INADDR_ANY);
+    }
+  } else if (uv_ip4_addr(interface_addr, 0, addr4) == 0) {
+    /* nothing, address was parsed */
+  } else if (uv_ip6_addr(interface_addr, 0, addr6) == 0) {
+    /* nothing, address was parsed */
+  } else {
+    return UV_EINVAL;
+  }
+
+  if (handle->socket == INVALID_SOCKET)
+    return UV_EBADF;
+
+  if (addr_st.ss_family == AF_INET) {
+    if (setsockopt(handle->socket,
+                   IPPROTO_IP,
+                   IP_MULTICAST_IF,
+                   (char*) &addr4->sin_addr,
+                   sizeof(addr4->sin_addr)) == SOCKET_ERROR) {
+      return uv_translate_sys_error(WSAGetLastError());
+    }
+  } else if (addr_st.ss_family == AF_INET6) {
+    if (setsockopt(handle->socket,
+                   IPPROTO_IPV6,
+                   IPV6_MULTICAST_IF,
+                   (char*) &addr6->sin6_scope_id,
+                   sizeof(addr6->sin6_scope_id)) == SOCKET_ERROR) {
+      return uv_translate_sys_error(WSAGetLastError());
+    }
+  } else {
+    assert(0 && "unexpected address family");
+    abort();
+  }
+
+  return 0;
+}
+
+
+int uv_udp_set_broadcast(uv_udp_t* handle, int value) {
+  BOOL optval = (BOOL) value;
+
+  if (handle->socket == INVALID_SOCKET)
+    return UV_EBADF;
+
+  if (setsockopt(handle->socket,
+                 SOL_SOCKET,
+                 SO_BROADCAST,
+                 (char*) &optval,
+                 sizeof optval)) {
+    return uv_translate_sys_error(WSAGetLastError());
+  }
+
+  return 0;
+}
+
+
+int uv__udp_is_bound(uv_udp_t* handle) {
+  struct sockaddr_storage addr;
+  int addrlen;
+
+  addrlen = sizeof(addr);
+  if (uv_udp_getsockname(handle, (struct sockaddr*) &addr, &addrlen) != 0)
+    return 0;
+
+  return addrlen > 0;
+}
+
+
+int uv_udp_open(uv_udp_t* handle, uv_os_sock_t sock) {
+  WSAPROTOCOL_INFOW protocol_info;
+  int opt_len;
+  int err;
+
+  /* Detect the address family of the socket. */
+  opt_len = (int) sizeof protocol_info;
+  if (getsockopt(sock,
+                 SOL_SOCKET,
+                 SO_PROTOCOL_INFOW,
+                 (char*) &protocol_info,
+                 &opt_len) == SOCKET_ERROR) {
+    return uv_translate_sys_error(GetLastError());
+  }
+
+  err = uv__udp_set_socket(handle->loop,
+                           handle,
+                           sock,
+                           protocol_info.iAddressFamily);
+  if (err)
+    return uv_translate_sys_error(err);
+
+  if (uv__udp_is_bound(handle))
+    handle->flags |= UV_HANDLE_BOUND;
+
+  if (uv__udp_is_connected(handle))
+    handle->flags |= UV_HANDLE_UDP_CONNECTED;
+
+  return 0;
+}
+
+
+#define SOCKOPT_SETTER(name, option4, option6, validate)                      \
+  int uv_udp_set_##name(uv_udp_t* handle, int value) {                        \
+    DWORD optval = (DWORD) value;                                             \
+                                                                              \
+    if (!(validate(value))) {                                                 \
+      return UV_EINVAL;                                                       \
+    }                                                                         \
+                                                                              \
+    if (handle->socket == INVALID_SOCKET)                                     \
+      return UV_EBADF;                                                        \
+                                                                              \
+    if (!(handle->flags & UV_HANDLE_IPV6)) {                                  \
+      /* Set IPv4 socket option */                                            \
+      if (setsockopt(handle->socket,                                          \
+                     IPPROTO_IP,                                              \
+                     option4,                                                 \
+                     (char*) &optval,                                         \
+                     sizeof optval)) {                                        \
+        return uv_translate_sys_error(WSAGetLastError());                     \
+      }                                                                       \
+    } else {                                                                  \
+      /* Set IPv6 socket option */                                            \
+      if (setsockopt(handle->socket,                                          \
+                     IPPROTO_IPV6,                                            \
+                     option6,                                                 \
+                     (char*) &optval,                                         \
+                     sizeof optval)) {                                        \
+        return uv_translate_sys_error(WSAGetLastError());                     \
+      }                                                                       \
+    }                                                                         \
+    return 0;                                                                 \
+  }
+
+#define VALIDATE_TTL(value) ((value) >= 1 && (value) <= 255)
+#define VALIDATE_MULTICAST_TTL(value) ((value) >= -1 && (value) <= 255)
+#define VALIDATE_MULTICAST_LOOP(value) (1)
+
+SOCKOPT_SETTER(ttl,
+               IP_TTL,
+               IPV6_HOPLIMIT,
+               VALIDATE_TTL)
+SOCKOPT_SETTER(multicast_ttl,
+               IP_MULTICAST_TTL,
+               IPV6_MULTICAST_HOPS,
+               VALIDATE_MULTICAST_TTL)
+SOCKOPT_SETTER(multicast_loop,
+               IP_MULTICAST_LOOP,
+               IPV6_MULTICAST_LOOP,
+               VALIDATE_MULTICAST_LOOP)
+
+#undef SOCKOPT_SETTER
+#undef VALIDATE_TTL
+#undef VALIDATE_MULTICAST_TTL
+#undef VALIDATE_MULTICAST_LOOP
+
+
+/* This function is an egress point, i.e. it returns libuv errors rather than
+ * system errors.
+ */
+int uv__udp_bind(uv_udp_t* handle,
+                 const struct sockaddr* addr,
+                 unsigned int addrlen,
+                 unsigned int flags) {
+  int err;
+
+  err = uv__udp_maybe_bind(handle, addr, addrlen, flags);
+  if (err)
+    return uv_translate_sys_error(err);
+
+  return 0;
+}
+
+
+int uv__udp_connect(uv_udp_t* handle,
+                    const struct sockaddr* addr,
+                    unsigned int addrlen) {
+  const struct sockaddr* bind_addr;
+  int err;
+
+  if (!(handle->flags & UV_HANDLE_BOUND)) {
+    if (addrlen == sizeof(uv_addr_ip4_any_))
+      bind_addr = (const struct sockaddr*) &uv_addr_ip4_any_;
+    else if (addrlen == sizeof(uv_addr_ip6_any_))
+      bind_addr = (const struct sockaddr*) &uv_addr_ip6_any_;
+    else
+      return UV_EINVAL;
+
+    err = uv__udp_maybe_bind(handle, bind_addr, addrlen, 0);
+    if (err)
+      return uv_translate_sys_error(err);
+  }
+
+  err = connect(handle->socket, addr, addrlen);
+  if (err)
+    return uv_translate_sys_error(WSAGetLastError());
+
+  handle->flags |= UV_HANDLE_UDP_CONNECTED;
+
+  return 0;
+}
+
+
+int uv__udp_disconnect(uv_udp_t* handle) {
+    int err;
+    struct sockaddr_storage addr;
+
+    memset(&addr, 0, sizeof(addr));
+
+    err = connect(handle->socket, (struct sockaddr*) &addr, sizeof(addr));
+    if (err)
+      return uv_translate_sys_error(WSAGetLastError());
+
+    handle->flags &= ~UV_HANDLE_UDP_CONNECTED;
+    return 0;
+}
+
+
+/* This function is an egress point, i.e. it returns libuv errors rather than
+ * system errors.
+ */
+int uv__udp_send(uv_udp_send_t* req,
+                 uv_udp_t* handle,
+                 const uv_buf_t bufs[],
+                 unsigned int nbufs,
+                 const struct sockaddr* addr,
+                 unsigned int addrlen,
+                 uv_udp_send_cb send_cb) {
+  const struct sockaddr* bind_addr;
+  int err;
+
+  if (!(handle->flags & UV_HANDLE_BOUND)) {
+    if (addrlen == sizeof(uv_addr_ip4_any_))
+      bind_addr = (const struct sockaddr*) &uv_addr_ip4_any_;
+    else if (addrlen == sizeof(uv_addr_ip6_any_))
+      bind_addr = (const struct sockaddr*) &uv_addr_ip6_any_;
+    else
+      return UV_EINVAL;
+
+    err = uv__udp_maybe_bind(handle, bind_addr, addrlen, 0);
+    if (err)
+      return uv_translate_sys_error(err);
+  }
+
+  err = uv__send(req, handle, bufs, nbufs, addr, addrlen, send_cb);
+  if (err)
+    return uv_translate_sys_error(err);
+
+  return 0;
+}
+
+
+int uv__udp_try_send(uv_udp_t* handle,
+                     const uv_buf_t bufs[],
+                     unsigned int nbufs,
+                     const struct sockaddr* addr,
+                     unsigned int addrlen) {
+  DWORD bytes;
+  const struct sockaddr* bind_addr;
+  struct sockaddr_storage converted;
+  int err;
+
+  assert(nbufs > 0);
+
+  if (addr != NULL) {
+    err = uv__convert_to_localhost_if_unspecified(addr, &converted);
+    if (err)
+      return err;
+    addr = (const struct sockaddr*) &converted;
+  }
+
+  /* Already sending a message.*/
+  if (handle->send_queue_count != 0)
+    return UV_EAGAIN;
+
+  if (!(handle->flags & UV_HANDLE_BOUND)) {
+    if (addrlen == sizeof(uv_addr_ip4_any_))
+      bind_addr = (const struct sockaddr*) &uv_addr_ip4_any_;
+    else if (addrlen == sizeof(uv_addr_ip6_any_))
+      bind_addr = (const struct sockaddr*) &uv_addr_ip6_any_;
+    else
+      return UV_EINVAL;
+    err = uv__udp_maybe_bind(handle, bind_addr, addrlen, 0);
+    if (err)
+      return uv_translate_sys_error(err);
+  }
+
+  err = WSASendTo(handle->socket,
+                  (WSABUF*)bufs,
+                  nbufs,
+                  &bytes,
+                  0,
+                  addr,
+                  addrlen,
+                  NULL,
+                  NULL);
+
+  if (err)
+    return uv_translate_sys_error(WSAGetLastError());
+
+  return bytes;
+}
diff --git a/wpinet/src/main/native/thirdparty/libuv/src/win/util.cpp b/wpinet/src/main/native/thirdparty/libuv/src/win/util.cpp
new file mode 100644
index 0000000..d9888ae
--- /dev/null
+++ b/wpinet/src/main/native/thirdparty/libuv/src/win/util.cpp
@@ -0,0 +1,1918 @@
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include <assert.h>
+#include <direct.h>
+#include <limits.h>
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include <wchar.h>
+
+#include "uv.h"
+#include "internal.h"
+
+/* clang-format off */
+#include <winsock2.h>
+#include <winperf.h>
+#include <iphlpapi.h>
+#include <psapi.h>
+#include <tlhelp32.h>
+#include <windows.h>
+/* clang-format on */
+#include <userenv.h>
+#include <math.h>
+
+/*
+ * Max title length; the only thing MSDN tells us about the maximum length
+ * of the console title is that it is smaller than 64K. However in practice
+ * it is much smaller, and there is no way to figure out what the exact length
+ * of the title is or can be, at least not on XP. To make it even more
+ * annoying, GetConsoleTitle fails when the buffer to be read into is bigger
+ * than the actual maximum length. So we make a conservative guess here;
+ * just don't put the novel you're writing in the title, unless the plot
+ * survives truncation.
+ */
+#define MAX_TITLE_LENGTH 8192
+
+/* The number of nanoseconds in one second. */
+#define UV__NANOSEC 1000000000
+
+/* Max user name length, from iphlpapi.h */
+#ifndef UNLEN
+# define UNLEN 256
+#endif
+
+
+/* A RtlGenRandom() by any other name... */
+extern "C" {
+extern BOOLEAN NTAPI SystemFunction036(PVOID Buffer, ULONG BufferLength);
+}
+
+/* Cached copy of the process title, plus a mutex guarding it. */
+static char *process_title;
+static CRITICAL_SECTION process_title_lock;
+
+#pragma comment(lib, "Advapi32.lib")
+#pragma comment(lib, "IPHLPAPI.lib")
+#pragma comment(lib, "Psapi.lib")
+#pragma comment(lib, "Userenv.lib")
+#pragma comment(lib, "kernel32.lib")
+
+/* Frequency of the high-resolution clock. */
+static uint64_t hrtime_frequency_ = 0;
+
+
+/*
+ * One-time initialization code for functionality defined in util.c.
+ */
+void uv__util_init(void) {
+  LARGE_INTEGER perf_frequency;
+
+  /* Initialize process title access mutex. */
+  InitializeCriticalSection(&process_title_lock);
+
+  /* Retrieve high-resolution timer frequency
+   * and precompute its reciprocal.
+   */
+  if (QueryPerformanceFrequency(&perf_frequency)) {
+    hrtime_frequency_ = perf_frequency.QuadPart;
+  } else {
+    uv_fatal_error(GetLastError(), "QueryPerformanceFrequency");
+  }
+}
+
+
+int uv_exepath(char* buffer, size_t* size_ptr) {
+  int utf8_len, utf16_buffer_len, utf16_len;
+  WCHAR* utf16_buffer;
+  int err;
+
+  if (buffer == NULL || size_ptr == NULL || *size_ptr == 0) {
+    return UV_EINVAL;
+  }
+
+  if (*size_ptr > 32768) {
+    /* Windows paths can never be longer than this. */
+    utf16_buffer_len = 32768;
+  } else {
+    utf16_buffer_len = (int) *size_ptr;
+  }
+
+  utf16_buffer = (WCHAR*) uv__malloc(sizeof(WCHAR) * utf16_buffer_len);
+  if (!utf16_buffer) {
+    return UV_ENOMEM;
+  }
+
+  /* Get the path as UTF-16. */
+  utf16_len = GetModuleFileNameW(NULL, utf16_buffer, utf16_buffer_len);
+  if (utf16_len <= 0) {
+    err = GetLastError();
+    goto error;
+  }
+
+  /* utf16_len contains the length, *not* including the terminating null. */
+  utf16_buffer[utf16_len] = L'\0';
+
+  /* Convert to UTF-8 */
+  utf8_len = WideCharToMultiByte(CP_UTF8,
+                                 0,
+                                 utf16_buffer,
+                                 -1,
+                                 buffer,
+                                 (int) *size_ptr,
+                                 NULL,
+                                 NULL);
+  if (utf8_len == 0) {
+    err = GetLastError();
+    goto error;
+  }
+
+  uv__free(utf16_buffer);
+
+  /* utf8_len *does* include the terminating null at this point, but the
+   * returned size shouldn't. */
+  *size_ptr = utf8_len - 1;
+  return 0;
+
+ error:
+  uv__free(utf16_buffer);
+  return uv_translate_sys_error(err);
+}
+
+
+int uv_cwd(char* buffer, size_t* size) {
+  DWORD utf16_len;
+  WCHAR *utf16_buffer;
+  int r;
+
+  if (buffer == NULL || size == NULL) {
+    return UV_EINVAL;
+  }
+
+  utf16_len = GetCurrentDirectoryW(0, NULL);
+  if (utf16_len == 0) {
+    return uv_translate_sys_error(GetLastError());
+  }
+  utf16_buffer = (WCHAR*)uv__malloc(utf16_len * sizeof(WCHAR));
+  if (utf16_buffer == NULL) {
+    return UV_ENOMEM;
+  }
+
+  utf16_len = GetCurrentDirectoryW(utf16_len, utf16_buffer);
+  if (utf16_len == 0) {
+    uv__free(utf16_buffer);
+    return uv_translate_sys_error(GetLastError());
+  }
+
+  /* utf16_len contains the length, *not* including the terminating null. */
+  utf16_buffer[utf16_len] = L'\0';
+
+  /* The returned directory should not have a trailing slash, unless it points
+   * at a drive root, like c:\. Remove it if needed. */
+  if (utf16_buffer[utf16_len - 1] == L'\\' &&
+      !(utf16_len == 3 && utf16_buffer[1] == L':')) {
+    utf16_len--;
+    utf16_buffer[utf16_len] = L'\0';
+  }
+
+  /* Check how much space we need */
+  r = WideCharToMultiByte(CP_UTF8,
+                          0,
+                          utf16_buffer,
+                          -1,
+                          NULL,
+                          0,
+                          NULL,
+                          NULL);
+  if (r == 0) {
+    uv__free(utf16_buffer);
+    return uv_translate_sys_error(GetLastError());
+  } else if (r > (int) *size) {
+    uv__free(utf16_buffer);
+    *size = r;
+    return UV_ENOBUFS;
+  }
+
+  /* Convert to UTF-8 */
+  r = WideCharToMultiByte(CP_UTF8,
+                          0,
+                          utf16_buffer,
+                          -1,
+                          buffer,
+                          *size > INT_MAX ? INT_MAX : (int) *size,
+                          NULL,
+                          NULL);
+  uv__free(utf16_buffer);
+
+  if (r == 0) {
+    return uv_translate_sys_error(GetLastError());
+  }
+
+  *size = r - 1;
+  return 0;
+}
+
+
+int uv_chdir(const char* dir) {
+  WCHAR *utf16_buffer;
+  size_t utf16_len, new_utf16_len;
+  WCHAR drive_letter, env_var[4];
+
+  if (dir == NULL) {
+    return UV_EINVAL;
+  }
+
+  utf16_len = MultiByteToWideChar(CP_UTF8,
+                                  0,
+                                  dir,
+                                  -1,
+                                  NULL,
+                                  0);
+  if (utf16_len == 0) {
+    return uv_translate_sys_error(GetLastError());
+  }
+  utf16_buffer = (WCHAR*)uv__malloc(utf16_len * sizeof(WCHAR));
+  if (utf16_buffer == NULL) {
+    return UV_ENOMEM;
+  }
+
+  if (MultiByteToWideChar(CP_UTF8,
+                          0,
+                          dir,
+                          -1,
+                          utf16_buffer,
+                          utf16_len) == 0) {
+    uv__free(utf16_buffer);
+    return uv_translate_sys_error(GetLastError());
+  }
+
+  if (!SetCurrentDirectoryW(utf16_buffer)) {
+    uv__free(utf16_buffer);
+    return uv_translate_sys_error(GetLastError());
+  }
+
+  /* Windows stores the drive-local path in an "hidden" environment variable,
+   * which has the form "=C:=C:\Windows". SetCurrentDirectory does not update
+   * this, so we'll have to do it. */
+  new_utf16_len = GetCurrentDirectoryW(utf16_len, utf16_buffer);
+  if (new_utf16_len > utf16_len ) {
+    uv__free(utf16_buffer);
+    utf16_buffer = (WCHAR*)uv__malloc(new_utf16_len * sizeof(WCHAR));
+    if (utf16_buffer == NULL) {
+      /* When updating the environment variable fails, return UV_OK anyway.
+       * We did successfully change current working directory, only updating
+       * hidden env variable failed. */
+      return 0;
+    }
+    new_utf16_len = GetCurrentDirectoryW(new_utf16_len, utf16_buffer);
+  }
+  if (utf16_len == 0) {
+    uv__free(utf16_buffer);
+    return 0;
+  }
+
+  /* The returned directory should not have a trailing slash, unless it points
+   * at a drive root, like c:\. Remove it if needed. */
+  if (utf16_buffer[utf16_len - 1] == L'\\' &&
+      !(utf16_len == 3 && utf16_buffer[1] == L':')) {
+    utf16_len--;
+    utf16_buffer[utf16_len] = L'\0';
+  }
+
+  if (utf16_len < 2 || utf16_buffer[1] != L':') {
+    /* Doesn't look like a drive letter could be there - probably an UNC path.
+     * TODO: Need to handle win32 namespaces like \\?\C:\ ? */
+    drive_letter = 0;
+  } else if (utf16_buffer[0] >= L'A' && utf16_buffer[0] <= L'Z') {
+    drive_letter = utf16_buffer[0];
+  } else if (utf16_buffer[0] >= L'a' && utf16_buffer[0] <= L'z') {
+    /* Convert to uppercase. */
+    drive_letter = utf16_buffer[0] - L'a' + L'A';
+  } else {
+    /* Not valid. */
+    drive_letter = 0;
+  }
+
+  if (drive_letter != 0) {
+    /* Construct the environment variable name and set it. */
+    env_var[0] = L'=';
+    env_var[1] = drive_letter;
+    env_var[2] = L':';
+    env_var[3] = L'\0';
+
+    SetEnvironmentVariableW(env_var, utf16_buffer);
+  }
+
+  uv__free(utf16_buffer);
+  return 0;
+}
+
+
+void uv_loadavg(double avg[3]) {
+  /* Can't be implemented */
+  avg[0] = avg[1] = avg[2] = 0;
+}
+
+
+uint64_t uv_get_free_memory(void) {
+  MEMORYSTATUSEX memory_status;
+  memory_status.dwLength = sizeof(memory_status);
+
+  if (!GlobalMemoryStatusEx(&memory_status)) {
+     return -1;
+  }
+
+  return (uint64_t)memory_status.ullAvailPhys;
+}
+
+
+uint64_t uv_get_total_memory(void) {
+  MEMORYSTATUSEX memory_status;
+  memory_status.dwLength = sizeof(memory_status);
+
+  if (!GlobalMemoryStatusEx(&memory_status)) {
+    return -1;
+  }
+
+  return (uint64_t)memory_status.ullTotalPhys;
+}
+
+
+uint64_t uv_get_constrained_memory(void) {
+  return 0;  /* Memory constraints are unknown. */
+}
+
+
+uv_pid_t uv_os_getpid(void) {
+  return GetCurrentProcessId();
+}
+
+
+uv_pid_t uv_os_getppid(void) {
+  int parent_pid = -1;
+  HANDLE handle;
+  PROCESSENTRY32 pe;
+  DWORD current_pid = GetCurrentProcessId();
+
+  pe.dwSize = sizeof(PROCESSENTRY32);
+  handle = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
+
+  if (Process32First(handle, &pe)) {
+    do {
+      if (pe.th32ProcessID == current_pid) {
+        parent_pid = pe.th32ParentProcessID;
+        break;
+      }
+    } while( Process32Next(handle, &pe));
+  }
+
+  CloseHandle(handle);
+  return parent_pid;
+}
+
+
+char** uv_setup_args(int argc, char** argv) {
+  return argv;
+}
+
+
+void uv__process_title_cleanup(void) {
+}
+
+
+int uv_set_process_title(const char* title) {
+  int err;
+  int length;
+  WCHAR* title_w = NULL;
+
+  uv__once_init();
+
+  /* Find out how big the buffer for the wide-char title must be */
+  length = MultiByteToWideChar(CP_UTF8, 0, title, -1, NULL, 0);
+  if (!length) {
+    err = GetLastError();
+    goto done;
+  }
+
+  /* Convert to wide-char string */
+  title_w = (WCHAR*)uv__malloc(sizeof(WCHAR) * length);
+  if (!title_w) {
+    uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc");
+  }
+
+  length = MultiByteToWideChar(CP_UTF8, 0, title, -1, title_w, length);
+  if (!length) {
+    err = GetLastError();
+    goto done;
+  }
+
+  /* If the title must be truncated insert a \0 terminator there */
+  if (length > MAX_TITLE_LENGTH) {
+    title_w[MAX_TITLE_LENGTH - 1] = L'\0';
+  }
+
+  if (!SetConsoleTitleW(title_w)) {
+    err = GetLastError();
+    goto done;
+  }
+
+  EnterCriticalSection(&process_title_lock);
+  uv__free(process_title);
+  process_title = uv__strdup(title);
+  LeaveCriticalSection(&process_title_lock);
+
+  err = 0;
+
+done:
+  uv__free(title_w);
+  return uv_translate_sys_error(err);
+}
+
+
+static int uv__get_process_title(void) {
+  WCHAR title_w[MAX_TITLE_LENGTH];
+
+  if (!GetConsoleTitleW(title_w, sizeof(title_w) / sizeof(WCHAR))) {
+    return -1;
+  }
+
+  if (uv__convert_utf16_to_utf8(title_w, -1, &process_title) != 0)
+    return -1;
+
+  return 0;
+}
+
+
+int uv_get_process_title(char* buffer, size_t size) {
+  size_t len;
+
+  if (buffer == NULL || size == 0)
+    return UV_EINVAL;
+
+  uv__once_init();
+
+  EnterCriticalSection(&process_title_lock);
+  /*
+   * If the process_title was never read before nor explicitly set,
+   * we must query it with getConsoleTitleW
+   */
+  if (!process_title && uv__get_process_title() == -1) {
+    LeaveCriticalSection(&process_title_lock);
+    return uv_translate_sys_error(GetLastError());
+  }
+
+  assert(process_title);
+  len = strlen(process_title) + 1;
+
+  if (size < len) {
+    LeaveCriticalSection(&process_title_lock);
+    return UV_ENOBUFS;
+  }
+
+  memcpy(buffer, process_title, len);
+  LeaveCriticalSection(&process_title_lock);
+
+  return 0;
+}
+
+
+uint64_t uv_hrtime(void) {
+  uv__once_init();
+  return uv__hrtime(UV__NANOSEC);
+}
+
+uint64_t uv__hrtime(unsigned int scale) {
+  LARGE_INTEGER counter;
+  double scaled_freq;
+  double result;
+
+  assert(hrtime_frequency_ != 0);
+  assert(scale != 0);
+  if (!QueryPerformanceCounter(&counter)) {
+    uv_fatal_error(GetLastError(), "QueryPerformanceCounter");
+  }
+  assert(counter.QuadPart != 0);
+
+  /* Because we have no guarantee about the order of magnitude of the
+   * performance counter interval, integer math could cause this computation
+   * to overflow. Therefore we resort to floating point math.
+   */
+  scaled_freq = (double) hrtime_frequency_ / scale;
+  result = (double) counter.QuadPart / scaled_freq;
+  return (uint64_t) result;
+}
+
+
+int uv_resident_set_memory(size_t* rss) {
+  HANDLE current_process;
+  PROCESS_MEMORY_COUNTERS pmc;
+
+  current_process = GetCurrentProcess();
+
+  if (!GetProcessMemoryInfo(current_process, &pmc, sizeof(pmc))) {
+    return uv_translate_sys_error(GetLastError());
+  }
+
+  *rss = pmc.WorkingSetSize;
+
+  return 0;
+}
+
+
+int uv_uptime(double* uptime) {
+  *uptime = GetTickCount64() / 1000.0;
+  return 0;
+}
+
+
+unsigned int uv_available_parallelism(void) {
+  SYSTEM_INFO info;
+  unsigned rc;
+
+  /* TODO(bnoordhuis) Use GetLogicalProcessorInformationEx() to support systems
+   * with > 64 CPUs? See https://github.com/libuv/libuv/pull/3458
+   */
+  GetSystemInfo(&info);
+
+  rc = info.dwNumberOfProcessors;
+  if (rc < 1)
+    rc = 1;
+
+  return rc;
+}
+
+
+int uv_cpu_info(uv_cpu_info_t** cpu_infos_ptr, int* cpu_count_ptr) {
+  uv_cpu_info_t* cpu_infos;
+  SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION* sppi;
+  DWORD sppi_size;
+  SYSTEM_INFO system_info;
+  DWORD cpu_count, i;
+  NTSTATUS status;
+  ULONG result_size;
+  int err;
+  uv_cpu_info_t* cpu_info;
+
+  cpu_infos = NULL;
+  cpu_count = 0;
+  sppi = NULL;
+
+  uv__once_init();
+
+  GetSystemInfo(&system_info);
+  cpu_count = system_info.dwNumberOfProcessors;
+
+  cpu_infos = (uv_cpu_info_t*)uv__calloc(cpu_count, sizeof *cpu_infos);
+  if (cpu_infos == NULL) {
+    err = ERROR_OUTOFMEMORY;
+    goto error;
+  }
+
+  sppi_size = cpu_count * sizeof(*sppi);
+  sppi = (SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION*)uv__malloc(sppi_size);
+  if (sppi == NULL) {
+    err = ERROR_OUTOFMEMORY;
+    goto error;
+  }
+
+  status = pNtQuerySystemInformation(SystemProcessorPerformanceInformation,
+                                     sppi,
+                                     sppi_size,
+                                     &result_size);
+  if (!NT_SUCCESS(status)) {
+    err = pRtlNtStatusToDosError(status);
+    goto error;
+  }
+
+  assert(result_size == sppi_size);
+
+  for (i = 0; i < cpu_count; i++) {
+    WCHAR key_name[128];
+    HKEY processor_key;
+    DWORD cpu_speed;
+    DWORD cpu_speed_size = sizeof(cpu_speed);
+    WCHAR cpu_brand[256];
+    DWORD cpu_brand_size = sizeof(cpu_brand);
+    size_t len;
+
+    len = _snwprintf(key_name,
+                     ARRAY_SIZE(key_name),
+                     L"HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\%d",
+                     i);
+
+    assert(len > 0 && len < ARRAY_SIZE(key_name));
+
+    err = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
+                        key_name,
+                        0,
+                        KEY_QUERY_VALUE,
+                        &processor_key);
+    if (err != ERROR_SUCCESS) {
+      goto error;
+    }
+
+    err = RegQueryValueExW(processor_key,
+                           L"~MHz",
+                           NULL,
+                           NULL,
+                           (BYTE*)&cpu_speed,
+                           &cpu_speed_size);
+    if (err != ERROR_SUCCESS) {
+      RegCloseKey(processor_key);
+      goto error;
+    }
+
+    err = RegQueryValueExW(processor_key,
+                           L"ProcessorNameString",
+                           NULL,
+                           NULL,
+                           (BYTE*)&cpu_brand,
+                           &cpu_brand_size);
+    RegCloseKey(processor_key);
+    if (err != ERROR_SUCCESS)
+      goto error;
+
+    cpu_info = &cpu_infos[i];
+    cpu_info->speed = cpu_speed;
+    cpu_info->cpu_times.user = sppi[i].UserTime.QuadPart / 10000;
+    cpu_info->cpu_times.sys = (sppi[i].KernelTime.QuadPart -
+        sppi[i].IdleTime.QuadPart) / 10000;
+    cpu_info->cpu_times.idle = sppi[i].IdleTime.QuadPart / 10000;
+    cpu_info->cpu_times.irq = sppi[i].InterruptTime.QuadPart / 10000;
+    cpu_info->cpu_times.nice = 0;
+
+    uv__convert_utf16_to_utf8(cpu_brand,
+                              cpu_brand_size / sizeof(WCHAR),
+                              &(cpu_info->model));
+  }
+
+  uv__free(sppi);
+
+  *cpu_count_ptr = cpu_count;
+  *cpu_infos_ptr = cpu_infos;
+
+  return 0;
+
+ error:
+  if (cpu_infos != NULL) {
+    /* This is safe because the cpu_infos array is zeroed on allocation. */
+    for (i = 0; i < cpu_count; i++)
+      uv__free(cpu_infos[i].model);
+  }
+
+  uv__free(cpu_infos);
+  uv__free(sppi);
+
+  return uv_translate_sys_error(err);
+}
+
+
+static int is_windows_version_or_greater(DWORD os_major,
+                                         DWORD os_minor,
+                                         WORD service_pack_major,
+                                         WORD service_pack_minor) {
+  OSVERSIONINFOEX osvi;
+  DWORDLONG condition_mask = 0;
+  int op = VER_GREATER_EQUAL;
+
+  /* Initialize the OSVERSIONINFOEX structure. */
+  ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX));
+  osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
+  osvi.dwMajorVersion = os_major;
+  osvi.dwMinorVersion = os_minor;
+  osvi.wServicePackMajor = service_pack_major;
+  osvi.wServicePackMinor = service_pack_minor;
+
+  /* Initialize the condition mask. */
+  VER_SET_CONDITION(condition_mask, VER_MAJORVERSION, op);
+  VER_SET_CONDITION(condition_mask, VER_MINORVERSION, op);
+  VER_SET_CONDITION(condition_mask, VER_SERVICEPACKMAJOR, op);
+  VER_SET_CONDITION(condition_mask, VER_SERVICEPACKMINOR, op);
+
+  /* Perform the test. */
+  return (int) VerifyVersionInfo(
+    &osvi,
+    VER_MAJORVERSION | VER_MINORVERSION |
+    VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR,
+    condition_mask);
+}
+
+
+static int address_prefix_match(int family,
+                                struct sockaddr* address,
+                                struct sockaddr* prefix_address,
+                                int prefix_len) {
+  uint8_t* address_data;
+  uint8_t* prefix_address_data;
+  int i;
+
+  assert(address->sa_family == family);
+  assert(prefix_address->sa_family == family);
+
+  if (family == AF_INET6) {
+    address_data = (uint8_t*) &(((struct sockaddr_in6 *) address)->sin6_addr);
+    prefix_address_data =
+      (uint8_t*) &(((struct sockaddr_in6 *) prefix_address)->sin6_addr);
+  } else {
+    address_data = (uint8_t*) &(((struct sockaddr_in *) address)->sin_addr);
+    prefix_address_data =
+      (uint8_t*) &(((struct sockaddr_in *) prefix_address)->sin_addr);
+  }
+
+  for (i = 0; i < prefix_len >> 3; i++) {
+    if (address_data[i] != prefix_address_data[i])
+      return 0;
+  }
+
+  if (prefix_len % 8)
+    return prefix_address_data[i] ==
+      (address_data[i] & (0xff << (8 - prefix_len % 8)));
+
+  return 1;
+}
+
+
+int uv_interface_addresses(uv_interface_address_t** addresses_ptr,
+    int* count_ptr) {
+  IP_ADAPTER_ADDRESSES* win_address_buf;
+  ULONG win_address_buf_size;
+  IP_ADAPTER_ADDRESSES* adapter;
+
+  uv_interface_address_t* uv_address_buf;
+  char* name_buf;
+  size_t uv_address_buf_size;
+  uv_interface_address_t* uv_address;
+
+  int count;
+
+  int is_vista_or_greater;
+  ULONG flags;
+
+  *addresses_ptr = NULL;
+  *count_ptr = 0;
+
+  is_vista_or_greater = is_windows_version_or_greater(6, 0, 0, 0);
+  if (is_vista_or_greater) {
+    flags = GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST |
+      GAA_FLAG_SKIP_DNS_SERVER;
+  } else {
+    /* We need at least XP SP1. */
+    if (!is_windows_version_or_greater(5, 1, 1, 0))
+      return UV_ENOTSUP;
+
+    flags = GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST |
+      GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_INCLUDE_PREFIX;
+  }
+
+
+  /* Fetch the size of the adapters reported by windows, and then get the list
+   * itself. */
+  win_address_buf_size = 0;
+  win_address_buf = NULL;
+
+  for (;;) {
+    ULONG r;
+
+    /* If win_address_buf is 0, then GetAdaptersAddresses will fail with.
+     * ERROR_BUFFER_OVERFLOW, and the required buffer size will be stored in
+     * win_address_buf_size. */
+    r = GetAdaptersAddresses(AF_UNSPEC,
+                             flags,
+                             NULL,
+                             win_address_buf,
+                             &win_address_buf_size);
+
+    if (r == ERROR_SUCCESS)
+      break;
+
+    uv__free(win_address_buf);
+
+    switch (r) {
+      case ERROR_BUFFER_OVERFLOW:
+        /* This happens when win_address_buf is NULL or too small to hold all
+         * adapters. */
+        win_address_buf =
+            (IP_ADAPTER_ADDRESSES*)uv__malloc(win_address_buf_size);
+        if (win_address_buf == NULL)
+          return UV_ENOMEM;
+
+        continue;
+
+      case ERROR_NO_DATA: {
+        /* No adapters were found. */
+        uv_address_buf = (uv_interface_address_t*)uv__malloc(1);
+        if (uv_address_buf == NULL)
+          return UV_ENOMEM;
+
+        *count_ptr = 0;
+        *addresses_ptr = uv_address_buf;
+
+        return 0;
+      }
+
+      case ERROR_ADDRESS_NOT_ASSOCIATED:
+        return UV_EAGAIN;
+
+      case ERROR_INVALID_PARAMETER:
+        /* MSDN says:
+         *   "This error is returned for any of the following conditions: the
+         *   SizePointer parameter is NULL, the Address parameter is not
+         *   AF_INET, AF_INET6, or AF_UNSPEC, or the address information for
+         *   the parameters requested is greater than ULONG_MAX."
+         * Since the first two conditions are not met, it must be that the
+         * adapter data is too big.
+         */
+        return UV_ENOBUFS;
+
+      default:
+        /* Other (unspecified) errors can happen, but we don't have any special
+         * meaning for them. */
+        assert(r != ERROR_SUCCESS);
+        return uv_translate_sys_error(r);
+    }
+  }
+
+  /* Count the number of enabled interfaces and compute how much space is
+   * needed to store their info. */
+  count = 0;
+  uv_address_buf_size = 0;
+
+  for (adapter = win_address_buf;
+       adapter != NULL;
+       adapter = adapter->Next) {
+    IP_ADAPTER_UNICAST_ADDRESS* unicast_address;
+    int name_size;
+
+    /* Interfaces that are not 'up' should not be reported. Also skip
+     * interfaces that have no associated unicast address, as to avoid
+     * allocating space for the name for this interface. */
+    if (adapter->OperStatus != IfOperStatusUp ||
+        adapter->FirstUnicastAddress == NULL)
+      continue;
+
+    /* Compute the size of the interface name. */
+    name_size = WideCharToMultiByte(CP_UTF8,
+                                    0,
+                                    adapter->FriendlyName,
+                                    -1,
+                                    NULL,
+                                    0,
+                                    NULL,
+                                    FALSE);
+    if (name_size <= 0) {
+      uv__free(win_address_buf);
+      return uv_translate_sys_error(GetLastError());
+    }
+    uv_address_buf_size += name_size;
+
+    /* Count the number of addresses associated with this interface, and
+     * compute the size. */
+    for (unicast_address = (IP_ADAPTER_UNICAST_ADDRESS*)
+                           adapter->FirstUnicastAddress;
+         unicast_address != NULL;
+         unicast_address = unicast_address->Next) {
+      count++;
+      uv_address_buf_size += sizeof(uv_interface_address_t);
+    }
+  }
+
+  /* Allocate space to store interface data plus adapter names. */
+  uv_address_buf = (uv_interface_address_t*)uv__malloc(uv_address_buf_size);
+  if (uv_address_buf == NULL) {
+    uv__free(win_address_buf);
+    return UV_ENOMEM;
+  }
+
+  /* Compute the start of the uv_interface_address_t array, and the place in
+   * the buffer where the interface names will be stored. */
+  uv_address = uv_address_buf;
+  name_buf = (char*) (uv_address_buf + count);
+
+  /* Fill out the output buffer. */
+  for (adapter = win_address_buf;
+       adapter != NULL;
+       adapter = adapter->Next) {
+    IP_ADAPTER_UNICAST_ADDRESS* unicast_address;
+    int name_size;
+    size_t max_name_size;
+
+    if (adapter->OperStatus != IfOperStatusUp ||
+        adapter->FirstUnicastAddress == NULL)
+      continue;
+
+    /* Convert the interface name to UTF8. */
+    max_name_size = (char*) uv_address_buf + uv_address_buf_size - name_buf;
+    if (max_name_size > (size_t) INT_MAX)
+      max_name_size = INT_MAX;
+    name_size = WideCharToMultiByte(CP_UTF8,
+                                    0,
+                                    adapter->FriendlyName,
+                                    -1,
+                                    name_buf,
+                                    (int) max_name_size,
+                                    NULL,
+                                    FALSE);
+    if (name_size <= 0) {
+      uv__free(win_address_buf);
+      uv__free(uv_address_buf);
+      return uv_translate_sys_error(GetLastError());
+    }
+
+    /* Add an uv_interface_address_t element for every unicast address. */
+    for (unicast_address = (IP_ADAPTER_UNICAST_ADDRESS*)
+                           adapter->FirstUnicastAddress;
+         unicast_address != NULL;
+         unicast_address = unicast_address->Next) {
+      struct sockaddr* sa;
+      ULONG prefix_len;
+
+      sa = unicast_address->Address.lpSockaddr;
+
+      /* XP has no OnLinkPrefixLength field. */
+      if (is_vista_or_greater) {
+        prefix_len =
+          ((IP_ADAPTER_UNICAST_ADDRESS_LH*) unicast_address)->OnLinkPrefixLength;
+      } else {
+        /* Prior to Windows Vista the FirstPrefix pointed to the list with
+         * single prefix for each IP address assigned to the adapter.
+         * Order of FirstPrefix does not match order of FirstUnicastAddress,
+         * so we need to find corresponding prefix.
+         */
+        IP_ADAPTER_PREFIX* prefix;
+        prefix_len = 0;
+
+        for (prefix = adapter->FirstPrefix; prefix; prefix = prefix->Next) {
+          /* We want the longest matching prefix. */
+          if (prefix->Address.lpSockaddr->sa_family != sa->sa_family ||
+              prefix->PrefixLength <= prefix_len)
+            continue;
+
+          if (address_prefix_match(sa->sa_family, sa,
+              prefix->Address.lpSockaddr, prefix->PrefixLength)) {
+            prefix_len = prefix->PrefixLength;
+          }
+        }
+
+        /* If there is no matching prefix information, return a single-host
+         * subnet mask (e.g. 255.255.255.255 for IPv4).
+         */
+        if (!prefix_len)
+          prefix_len = (sa->sa_family == AF_INET6) ? 128 : 32;
+      }
+
+      memset(uv_address, 0, sizeof *uv_address);
+
+      uv_address->name = name_buf;
+
+      if (adapter->PhysicalAddressLength == sizeof(uv_address->phys_addr)) {
+        memcpy(uv_address->phys_addr,
+               adapter->PhysicalAddress,
+               sizeof(uv_address->phys_addr));
+      }
+
+      uv_address->is_internal =
+          (adapter->IfType == IF_TYPE_SOFTWARE_LOOPBACK);
+
+      if (sa->sa_family == AF_INET6) {
+        uv_address->address.address6 = *((struct sockaddr_in6 *) sa);
+
+        uv_address->netmask.netmask6.sin6_family = AF_INET6;
+        memset(uv_address->netmask.netmask6.sin6_addr.s6_addr, 0xff, prefix_len >> 3);
+        /* This check ensures that we don't write past the size of the data. */
+        if (prefix_len % 8) {
+          uv_address->netmask.netmask6.sin6_addr.s6_addr[prefix_len >> 3] =
+              0xff << (8 - prefix_len % 8);
+        }
+
+      } else {
+        uv_address->address.address4 = *((struct sockaddr_in *) sa);
+
+        uv_address->netmask.netmask4.sin_family = AF_INET;
+        uv_address->netmask.netmask4.sin_addr.s_addr = (prefix_len > 0) ?
+            htonl(0xffffffff << (32 - prefix_len)) : 0;
+      }
+
+      uv_address++;
+    }
+
+    name_buf += name_size;
+  }
+
+  uv__free(win_address_buf);
+
+  *addresses_ptr = uv_address_buf;
+  *count_ptr = count;
+
+  return 0;
+}
+
+
+void uv_free_interface_addresses(uv_interface_address_t* addresses,
+    int count) {
+  uv__free(addresses);
+}
+
+
+int uv_getrusage(uv_rusage_t *uv_rusage) {
+  FILETIME createTime, exitTime, kernelTime, userTime;
+  SYSTEMTIME kernelSystemTime, userSystemTime;
+  PROCESS_MEMORY_COUNTERS memCounters;
+  IO_COUNTERS ioCounters;
+  int ret;
+
+  ret = GetProcessTimes(GetCurrentProcess(), &createTime, &exitTime, &kernelTime, &userTime);
+  if (ret == 0) {
+    return uv_translate_sys_error(GetLastError());
+  }
+
+  ret = FileTimeToSystemTime(&kernelTime, &kernelSystemTime);
+  if (ret == 0) {
+    return uv_translate_sys_error(GetLastError());
+  }
+
+  ret = FileTimeToSystemTime(&userTime, &userSystemTime);
+  if (ret == 0) {
+    return uv_translate_sys_error(GetLastError());
+  }
+
+  ret = GetProcessMemoryInfo(GetCurrentProcess(),
+                             &memCounters,
+                             sizeof(memCounters));
+  if (ret == 0) {
+    return uv_translate_sys_error(GetLastError());
+  }
+
+  ret = GetProcessIoCounters(GetCurrentProcess(), &ioCounters);
+  if (ret == 0) {
+    return uv_translate_sys_error(GetLastError());
+  }
+
+  memset(uv_rusage, 0, sizeof(*uv_rusage));
+
+  uv_rusage->ru_utime.tv_sec = userSystemTime.wHour * 3600 +
+                               userSystemTime.wMinute * 60 +
+                               userSystemTime.wSecond;
+  uv_rusage->ru_utime.tv_usec = userSystemTime.wMilliseconds * 1000;
+
+  uv_rusage->ru_stime.tv_sec = kernelSystemTime.wHour * 3600 +
+                               kernelSystemTime.wMinute * 60 +
+                               kernelSystemTime.wSecond;
+  uv_rusage->ru_stime.tv_usec = kernelSystemTime.wMilliseconds * 1000;
+
+  uv_rusage->ru_majflt = (uint64_t) memCounters.PageFaultCount;
+  uv_rusage->ru_maxrss = (uint64_t) memCounters.PeakWorkingSetSize / 1024;
+
+  uv_rusage->ru_oublock = (uint64_t) ioCounters.WriteOperationCount;
+  uv_rusage->ru_inblock = (uint64_t) ioCounters.ReadOperationCount;
+
+  return 0;
+}
+
+
+int uv_os_homedir(char* buffer, size_t* size) {
+  uv_passwd_t pwd;
+  size_t len;
+  int r;
+
+  /* Check if the USERPROFILE environment variable is set first. The task of
+     performing input validation on buffer and size is taken care of by
+     uv_os_getenv(). */
+  r = uv_os_getenv("USERPROFILE", buffer, size);
+
+  /* Don't return an error if USERPROFILE was not found. */
+  if (r != UV_ENOENT)
+    return r;
+
+  /* USERPROFILE is not set, so call uv__getpwuid_r() */
+  r = uv__getpwuid_r(&pwd);
+
+  if (r != 0) {
+    return r;
+  }
+
+  len = strlen(pwd.homedir);
+
+  if (len >= *size) {
+    *size = len + 1;
+    uv_os_free_passwd(&pwd);
+    return UV_ENOBUFS;
+  }
+
+  memcpy(buffer, pwd.homedir, len + 1);
+  *size = len;
+  uv_os_free_passwd(&pwd);
+
+  return 0;
+}
+
+
+int uv_os_tmpdir(char* buffer, size_t* size) {
+  wchar_t *path;
+  DWORD bufsize;
+  size_t len;
+
+  if (buffer == NULL || size == NULL || *size == 0)
+    return UV_EINVAL;
+
+  len = 0;
+  len = GetTempPathW(0, NULL);
+  if (len == 0) {
+    return uv_translate_sys_error(GetLastError());
+  }
+  /* Include space for terminating null char. */
+  len += 1;
+  path = (wchar_t*)uv__malloc(len * sizeof(wchar_t));
+  if (path == NULL) {
+    return UV_ENOMEM;
+  }
+  len  = GetTempPathW(len, path);
+
+  if (len == 0) {
+    uv__free(path);
+    return uv_translate_sys_error(GetLastError());
+  }
+
+  /* The returned directory should not have a trailing slash, unless it points
+   * at a drive root, like c:\. Remove it if needed. */
+  if (path[len - 1] == L'\\' &&
+      !(len == 3 && path[1] == L':')) {
+    len--;
+    path[len] = L'\0';
+  }
+
+  /* Check how much space we need */
+  bufsize = WideCharToMultiByte(CP_UTF8, 0, path, -1, NULL, 0, NULL, NULL);
+
+  if (bufsize == 0) {
+    uv__free(path);
+    return uv_translate_sys_error(GetLastError());
+  } else if (bufsize > *size) {
+    uv__free(path);
+    *size = bufsize;
+    return UV_ENOBUFS;
+  }
+
+  /* Convert to UTF-8 */
+  bufsize = WideCharToMultiByte(CP_UTF8,
+                                0,
+                                path,
+                                -1,
+                                buffer,
+                                *size,
+                                NULL,
+                                NULL);
+  uv__free(path);
+
+  if (bufsize == 0)
+    return uv_translate_sys_error(GetLastError());
+
+  *size = bufsize - 1;
+  return 0;
+}
+
+
+void uv_os_free_passwd(uv_passwd_t* pwd) {
+  if (pwd == NULL)
+    return;
+
+  uv__free(pwd->username);
+  uv__free(pwd->homedir);
+  pwd->username = NULL;
+  pwd->homedir = NULL;
+}
+
+
+/*
+ * Converts a UTF-16 string into a UTF-8 one. The resulting string is
+ * null-terminated.
+ *
+ * If utf16 is null terminated, utf16len can be set to -1, otherwise it must
+ * be specified.
+ */
+int uv__convert_utf16_to_utf8(const WCHAR* utf16, int utf16len, char** utf8) {
+  DWORD bufsize;
+
+  if (utf16 == NULL)
+    return UV_EINVAL;
+
+  /* Check how much space we need */
+  bufsize = WideCharToMultiByte(CP_UTF8,
+                                0,
+                                utf16,
+                                utf16len,
+                                NULL,
+                                0,
+                                NULL,
+                                NULL);
+
+  if (bufsize == 0)
+    return uv_translate_sys_error(GetLastError());
+
+  /* Allocate the destination buffer adding an extra byte for the terminating
+   * NULL. If utf16len is not -1 WideCharToMultiByte will not add it, so
+   * we do it ourselves always, just in case. */
+  *utf8 = (char*)uv__malloc(bufsize + 1);
+
+  if (*utf8 == NULL)
+    return UV_ENOMEM;
+
+  /* Convert to UTF-8 */
+  bufsize = WideCharToMultiByte(CP_UTF8,
+                                0,
+                                utf16,
+                                utf16len,
+                                *utf8,
+                                bufsize,
+                                NULL,
+                                NULL);
+
+  if (bufsize == 0) {
+    uv__free(*utf8);
+    *utf8 = NULL;
+    return uv_translate_sys_error(GetLastError());
+  }
+
+  (*utf8)[bufsize] = '\0';
+  return 0;
+}
+
+
+/*
+ * Converts a UTF-8 string into a UTF-16 one. The resulting string is
+ * null-terminated.
+ *
+ * If utf8 is null terminated, utf8len can be set to -1, otherwise it must
+ * be specified.
+ */
+int uv__convert_utf8_to_utf16(const char* utf8, int utf8len, WCHAR** utf16) {
+  int bufsize;
+
+  if (utf8 == NULL)
+    return UV_EINVAL;
+
+  /* Check how much space we need */
+  bufsize = MultiByteToWideChar(CP_UTF8, 0, utf8, utf8len, NULL, 0);
+
+  if (bufsize == 0)
+    return uv_translate_sys_error(GetLastError());
+
+  /* Allocate the destination buffer adding an extra byte for the terminating
+   * NULL. If utf8len is not -1 MultiByteToWideChar will not add it, so
+   * we do it ourselves always, just in case. */
+  *utf16 = (WCHAR*)uv__malloc(sizeof(WCHAR) * (bufsize + 1));
+
+  if (*utf16 == NULL)
+    return UV_ENOMEM;
+
+  /* Convert to UTF-16 */
+  bufsize = MultiByteToWideChar(CP_UTF8, 0, utf8, utf8len, *utf16, bufsize);
+
+  if (bufsize == 0) {
+    uv__free(*utf16);
+    *utf16 = NULL;
+    return uv_translate_sys_error(GetLastError());
+  }
+
+  (*utf16)[bufsize] = L'\0';
+  return 0;
+}
+
+
+int uv__getpwuid_r(uv_passwd_t* pwd) {
+  HANDLE token;
+  wchar_t username[UNLEN + 1];
+  wchar_t *path;
+  DWORD bufsize;
+  int r;
+
+  if (pwd == NULL)
+    return UV_EINVAL;
+
+  /* Get the home directory using GetUserProfileDirectoryW() */
+  if (OpenProcessToken(GetCurrentProcess(), TOKEN_READ, &token) == 0)
+    return uv_translate_sys_error(GetLastError());
+
+  bufsize = 0;
+  GetUserProfileDirectoryW(token, NULL, &bufsize);
+  if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
+    r = GetLastError();
+    CloseHandle(token);
+    return uv_translate_sys_error(r);
+  }
+
+  path = (wchar_t*)uv__malloc(bufsize * sizeof(wchar_t));
+  if (path == NULL) {
+    CloseHandle(token);
+    return UV_ENOMEM;
+  }
+
+  if (!GetUserProfileDirectoryW(token, path, &bufsize)) {
+    r = GetLastError();
+    CloseHandle(token);
+    uv__free(path);
+    return uv_translate_sys_error(r);
+  }
+
+  CloseHandle(token);
+
+  /* Get the username using GetUserNameW() */
+  bufsize = ARRAY_SIZE(username);
+  if (!GetUserNameW(username, &bufsize)) {
+    r = GetLastError();
+    uv__free(path);
+
+    /* This should not be possible */
+    if (r == ERROR_INSUFFICIENT_BUFFER)
+      return UV_ENOMEM;
+
+    return uv_translate_sys_error(r);
+  }
+
+  pwd->homedir = NULL;
+  r = uv__convert_utf16_to_utf8(path, -1, &pwd->homedir);
+  uv__free(path);
+
+  if (r != 0)
+    return r;
+
+  pwd->username = NULL;
+  r = uv__convert_utf16_to_utf8(username, -1, &pwd->username);
+
+  if (r != 0) {
+    uv__free(pwd->homedir);
+    return r;
+  }
+
+  pwd->shell = NULL;
+  pwd->uid = -1;
+  pwd->gid = -1;
+
+  return 0;
+}
+
+
+int uv_os_get_passwd(uv_passwd_t* pwd) {
+  return uv__getpwuid_r(pwd);
+}
+
+
+int uv_os_environ(uv_env_item_t** envitems, int* count) {
+  wchar_t* env;
+  wchar_t* penv;
+  int i, cnt;
+  uv_env_item_t* envitem;
+
+  *envitems = NULL;
+  *count = 0;
+
+  env = GetEnvironmentStringsW();
+  if (env == NULL)
+    return 0;
+
+  for (penv = env, i = 0; *penv != L'\0'; penv += wcslen(penv) + 1, i++);
+
+  *envitems = (uv_env_item_t*)uv__calloc(i, sizeof(**envitems));
+  if (*envitems == NULL) {
+    FreeEnvironmentStringsW(env);
+    return UV_ENOMEM;
+  }
+
+  penv = env;
+  cnt = 0;
+
+  while (*penv != L'\0' && cnt < i) {
+    char* buf;
+    char* ptr;
+
+    if (uv__convert_utf16_to_utf8(penv, -1, &buf) != 0)
+      goto fail;
+
+    /* Using buf + 1 here because we know that `buf` has length at least 1,
+     * and some special environment variables on Windows start with a = sign. */
+    ptr = strchr(buf + 1, '=');
+    if (ptr == NULL) {
+      uv__free(buf);
+      goto do_continue;
+    }
+
+    *ptr = '\0';
+
+    envitem = &(*envitems)[cnt];
+    envitem->name = buf;
+    envitem->value = ptr + 1;
+
+    cnt++;
+
+  do_continue:
+    penv += wcslen(penv) + 1;
+  }
+
+  FreeEnvironmentStringsW(env);
+
+  *count = cnt;
+  return 0;
+
+fail:
+  FreeEnvironmentStringsW(env);
+
+  for (i = 0; i < cnt; i++) {
+    envitem = &(*envitems)[cnt];
+    uv__free(envitem->name);
+  }
+  uv__free(*envitems);
+
+  *envitems = NULL;
+  *count = 0;
+  return UV_ENOMEM;
+}
+
+
+int uv_os_getenv(const char* name, char* buffer, size_t* size) {
+  wchar_t fastvar[512];
+  wchar_t* var;
+  DWORD varlen;
+  wchar_t* name_w;
+  DWORD bufsize;
+  size_t len;
+  int r;
+
+  if (name == NULL || buffer == NULL || size == NULL || *size == 0)
+    return UV_EINVAL;
+
+  r = uv__convert_utf8_to_utf16(name, -1, &name_w);
+
+  if (r != 0)
+    return r;
+
+  var = fastvar;
+  varlen = ARRAY_SIZE(fastvar);
+
+  for (;;) {
+    SetLastError(ERROR_SUCCESS);
+    len = GetEnvironmentVariableW(name_w, var, varlen);
+
+    if (len < varlen)
+      break;
+
+    /* Try repeatedly because we might have been preempted by another thread
+     * modifying the environment variable just as we're trying to read it.
+     */
+    if (var != fastvar)
+      uv__free(var);
+
+    varlen = 1 + len;
+    var = (wchar_t*)uv__malloc(varlen * sizeof(*var));
+
+    if (var == NULL) {
+      r = UV_ENOMEM;
+      goto fail;
+    }
+  }
+
+  uv__free(name_w);
+  name_w = NULL;
+
+  if (len == 0) {
+    r = GetLastError();
+    if (r != ERROR_SUCCESS) {
+      r = uv_translate_sys_error(r);
+      goto fail;
+    }
+  }
+
+  /* Check how much space we need */
+  bufsize = WideCharToMultiByte(CP_UTF8, 0, var, -1, NULL, 0, NULL, NULL);
+
+  if (bufsize == 0) {
+    r = uv_translate_sys_error(GetLastError());
+    goto fail;
+  } else if (bufsize > *size) {
+    *size = bufsize;
+    r = UV_ENOBUFS;
+    goto fail;
+  }
+
+  /* Convert to UTF-8 */
+  bufsize = WideCharToMultiByte(CP_UTF8,
+                                0,
+                                var,
+                                -1,
+                                buffer,
+                                *size,
+                                NULL,
+                                NULL);
+
+  if (bufsize == 0) {
+    r = uv_translate_sys_error(GetLastError());
+    goto fail;
+  }
+
+  *size = bufsize - 1;
+  r = 0;
+
+fail:
+
+  if (name_w != NULL)
+    uv__free(name_w);
+
+  if (var != fastvar)
+    uv__free(var);
+
+  return r;
+}
+
+
+int uv_os_setenv(const char* name, const char* value) {
+  wchar_t* name_w;
+  wchar_t* value_w;
+  int r;
+
+  if (name == NULL || value == NULL)
+    return UV_EINVAL;
+
+  r = uv__convert_utf8_to_utf16(name, -1, &name_w);
+
+  if (r != 0)
+    return r;
+
+  r = uv__convert_utf8_to_utf16(value, -1, &value_w);
+
+  if (r != 0) {
+    uv__free(name_w);
+    return r;
+  }
+
+  r = SetEnvironmentVariableW(name_w, value_w);
+  uv__free(name_w);
+  uv__free(value_w);
+
+  if (r == 0)
+    return uv_translate_sys_error(GetLastError());
+
+  return 0;
+}
+
+
+int uv_os_unsetenv(const char* name) {
+  wchar_t* name_w;
+  int r;
+
+  if (name == NULL)
+    return UV_EINVAL;
+
+  r = uv__convert_utf8_to_utf16(name, -1, &name_w);
+
+  if (r != 0)
+    return r;
+
+  r = SetEnvironmentVariableW(name_w, NULL);
+  uv__free(name_w);
+
+  if (r == 0)
+    return uv_translate_sys_error(GetLastError());
+
+  return 0;
+}
+
+
+int uv_os_gethostname(char* buffer, size_t* size) {
+  WCHAR buf[UV_MAXHOSTNAMESIZE];
+  size_t len;
+  char* utf8_str;
+  int convert_result;
+
+  if (buffer == NULL || size == NULL || *size == 0)
+    return UV_EINVAL;
+
+  uv__once_init(); /* Initialize winsock */
+
+  if (pGetHostNameW == NULL)
+    return UV_ENOSYS;
+
+  if (pGetHostNameW(buf, UV_MAXHOSTNAMESIZE) != 0)
+    return uv_translate_sys_error(WSAGetLastError());
+
+  convert_result = uv__convert_utf16_to_utf8(buf, -1, &utf8_str);
+
+  if (convert_result != 0)
+    return convert_result;
+
+  len = strlen(utf8_str);
+  if (len >= *size) {
+    *size = len + 1;
+    uv__free(utf8_str);
+    return UV_ENOBUFS;
+  }
+
+  memcpy(buffer, utf8_str, len + 1);
+  uv__free(utf8_str);
+  *size = len;
+  return 0;
+}
+
+
+static int uv__get_handle(uv_pid_t pid, int access, HANDLE* handle) {
+  int r;
+
+  if (pid == 0)
+    *handle = GetCurrentProcess();
+  else
+    *handle = OpenProcess(access, FALSE, pid);
+
+  if (*handle == NULL) {
+    r = GetLastError();
+
+    if (r == ERROR_INVALID_PARAMETER)
+      return UV_ESRCH;
+    else
+      return uv_translate_sys_error(r);
+  }
+
+  return 0;
+}
+
+
+int uv_os_getpriority(uv_pid_t pid, int* priority) {
+  HANDLE handle;
+  int r;
+
+  if (priority == NULL)
+    return UV_EINVAL;
+
+  r = uv__get_handle(pid, PROCESS_QUERY_LIMITED_INFORMATION, &handle);
+
+  if (r != 0)
+    return r;
+
+  r = GetPriorityClass(handle);
+
+  if (r == 0) {
+    r = uv_translate_sys_error(GetLastError());
+  } else {
+    /* Map Windows priority classes to Unix nice values. */
+    if (r == REALTIME_PRIORITY_CLASS)
+      *priority = UV_PRIORITY_HIGHEST;
+    else if (r == HIGH_PRIORITY_CLASS)
+      *priority = UV_PRIORITY_HIGH;
+    else if (r == ABOVE_NORMAL_PRIORITY_CLASS)
+      *priority = UV_PRIORITY_ABOVE_NORMAL;
+    else if (r == NORMAL_PRIORITY_CLASS)
+      *priority = UV_PRIORITY_NORMAL;
+    else if (r == BELOW_NORMAL_PRIORITY_CLASS)
+      *priority = UV_PRIORITY_BELOW_NORMAL;
+    else  /* IDLE_PRIORITY_CLASS */
+      *priority = UV_PRIORITY_LOW;
+
+    r = 0;
+  }
+
+  CloseHandle(handle);
+  return r;
+}
+
+
+int uv_os_setpriority(uv_pid_t pid, int priority) {
+  HANDLE handle;
+  int priority_class;
+  int r;
+
+  /* Map Unix nice values to Windows priority classes. */
+  if (priority < UV_PRIORITY_HIGHEST || priority > UV_PRIORITY_LOW)
+    return UV_EINVAL;
+  else if (priority < UV_PRIORITY_HIGH)
+    priority_class = REALTIME_PRIORITY_CLASS;
+  else if (priority < UV_PRIORITY_ABOVE_NORMAL)
+    priority_class = HIGH_PRIORITY_CLASS;
+  else if (priority < UV_PRIORITY_NORMAL)
+    priority_class = ABOVE_NORMAL_PRIORITY_CLASS;
+  else if (priority < UV_PRIORITY_BELOW_NORMAL)
+    priority_class = NORMAL_PRIORITY_CLASS;
+  else if (priority < UV_PRIORITY_LOW)
+    priority_class = BELOW_NORMAL_PRIORITY_CLASS;
+  else
+    priority_class = IDLE_PRIORITY_CLASS;
+
+  r = uv__get_handle(pid, PROCESS_SET_INFORMATION, &handle);
+
+  if (r != 0)
+    return r;
+
+  if (SetPriorityClass(handle, priority_class) == 0)
+    r = uv_translate_sys_error(GetLastError());
+
+  CloseHandle(handle);
+  return r;
+}
+
+
+int uv_os_uname(uv_utsname_t* buffer) {
+  /* Implementation loosely based on
+     https://github.com/gagern/gnulib/blob/master/lib/uname.c */
+  OSVERSIONINFOW os_info;
+  SYSTEM_INFO system_info;
+  HKEY registry_key;
+  WCHAR product_name_w[256];
+  DWORD product_name_w_size;
+  int version_size;
+  int processor_level;
+  int r;
+
+  if (buffer == NULL)
+    return UV_EINVAL;
+
+  uv__once_init();
+  os_info.dwOSVersionInfoSize = sizeof(os_info);
+  os_info.szCSDVersion[0] = L'\0';
+
+  /* Try calling RtlGetVersion(), and fall back to the deprecated GetVersionEx()
+     if RtlGetVersion() is not available. */
+  if (pRtlGetVersion) {
+    pRtlGetVersion(&os_info);
+  } else {
+    /* Silence GetVersionEx() deprecation warning. */
+    #ifdef _MSC_VER
+    #pragma warning(disable : 4996)
+    #endif
+    if (GetVersionExW(&os_info) == 0) {
+      r = uv_translate_sys_error(GetLastError());
+      goto error;
+    }
+  }
+
+  /* Populate the version field. */
+  version_size = 0;
+  r = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
+                    L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion",
+                    0,
+                    KEY_QUERY_VALUE,
+                    &registry_key);
+
+  if (r == ERROR_SUCCESS) {
+    product_name_w_size = sizeof(product_name_w);
+    r = RegGetValueW(registry_key,
+                     NULL,
+                     L"ProductName",
+                     RRF_RT_REG_SZ,
+                     NULL,
+                     (PVOID) product_name_w,
+                     &product_name_w_size);
+    RegCloseKey(registry_key);
+
+    if (r == ERROR_SUCCESS) {
+      version_size = WideCharToMultiByte(CP_UTF8,
+                                         0,
+                                         product_name_w,
+                                         -1,
+                                         buffer->version,
+                                         sizeof(buffer->version),
+                                         NULL,
+                                         NULL);
+      if (version_size == 0) {
+        r = uv_translate_sys_error(GetLastError());
+        goto error;
+      }
+    }
+  }
+
+  /* Append service pack information to the version if present. */
+  if (os_info.szCSDVersion[0] != L'\0') {
+    if (version_size > 0)
+      buffer->version[version_size - 1] = ' ';
+
+    if (WideCharToMultiByte(CP_UTF8,
+                            0,
+                            os_info.szCSDVersion,
+                            -1,
+                            buffer->version + version_size,
+                            sizeof(buffer->version) - version_size,
+                            NULL,
+                            NULL) == 0) {
+      r = uv_translate_sys_error(GetLastError());
+      goto error;
+    }
+  }
+
+  /* Populate the sysname field. */
+#ifdef __MINGW32__
+  r = snprintf(buffer->sysname,
+               sizeof(buffer->sysname),
+               "MINGW32_NT-%u.%u",
+               (unsigned int) os_info.dwMajorVersion,
+               (unsigned int) os_info.dwMinorVersion);
+  assert((size_t)r < sizeof(buffer->sysname));
+#else
+  uv__strscpy(buffer->sysname, "Windows_NT", sizeof(buffer->sysname));
+#endif
+
+  /* Populate the release field. */
+  r = snprintf(buffer->release,
+               sizeof(buffer->release),
+               "%d.%d.%d",
+               (unsigned int) os_info.dwMajorVersion,
+               (unsigned int) os_info.dwMinorVersion,
+               (unsigned int) os_info.dwBuildNumber);
+  assert((size_t)r < sizeof(buffer->release));
+
+  /* Populate the machine field. */
+  GetSystemInfo(&system_info);
+
+  switch (system_info.wProcessorArchitecture) {
+    case PROCESSOR_ARCHITECTURE_AMD64:
+      uv__strscpy(buffer->machine, "x86_64", sizeof(buffer->machine));
+      break;
+    case PROCESSOR_ARCHITECTURE_IA64:
+      uv__strscpy(buffer->machine, "ia64", sizeof(buffer->machine));
+      break;
+    case PROCESSOR_ARCHITECTURE_INTEL:
+      uv__strscpy(buffer->machine, "i386", sizeof(buffer->machine));
+
+      if (system_info.wProcessorLevel > 3) {
+        processor_level = system_info.wProcessorLevel < 6 ?
+                          system_info.wProcessorLevel : 6;
+        buffer->machine[1] = '0' + processor_level;
+      }
+
+      break;
+    case PROCESSOR_ARCHITECTURE_IA32_ON_WIN64:
+      uv__strscpy(buffer->machine, "i686", sizeof(buffer->machine));
+      break;
+    case PROCESSOR_ARCHITECTURE_MIPS:
+      uv__strscpy(buffer->machine, "mips", sizeof(buffer->machine));
+      break;
+    case PROCESSOR_ARCHITECTURE_ALPHA:
+    case PROCESSOR_ARCHITECTURE_ALPHA64:
+      uv__strscpy(buffer->machine, "alpha", sizeof(buffer->machine));
+      break;
+    case PROCESSOR_ARCHITECTURE_PPC:
+      uv__strscpy(buffer->machine, "powerpc", sizeof(buffer->machine));
+      break;
+    case PROCESSOR_ARCHITECTURE_SHX:
+      uv__strscpy(buffer->machine, "sh", sizeof(buffer->machine));
+      break;
+    case PROCESSOR_ARCHITECTURE_ARM:
+      uv__strscpy(buffer->machine, "arm", sizeof(buffer->machine));
+      break;
+    default:
+      uv__strscpy(buffer->machine, "unknown", sizeof(buffer->machine));
+      break;
+  }
+
+  return 0;
+
+error:
+  buffer->sysname[0] = '\0';
+  buffer->release[0] = '\0';
+  buffer->version[0] = '\0';
+  buffer->machine[0] = '\0';
+  return r;
+}
+
+int uv_gettimeofday(uv_timeval64_t* tv) {
+  /* Based on https://doxygen.postgresql.org/gettimeofday_8c_source.html */
+  const uint64_t epoch = (uint64_t) 116444736000000000ULL;
+  FILETIME file_time;
+  ULARGE_INTEGER ularge;
+
+  if (tv == NULL)
+    return UV_EINVAL;
+
+  GetSystemTimeAsFileTime(&file_time);
+  ularge.LowPart = file_time.dwLowDateTime;
+  ularge.HighPart = file_time.dwHighDateTime;
+  tv->tv_sec = (int64_t) ((ularge.QuadPart - epoch) / 10000000L);
+  tv->tv_usec = (int32_t) (((ularge.QuadPart - epoch) % 10000000L) / 10);
+  return 0;
+}
+
+int uv__random_rtlgenrandom(void* buf, size_t buflen) {
+  if (buflen == 0)
+    return 0;
+
+  if (SystemFunction036(buf, buflen) == FALSE)
+    return UV_EIO;
+
+  return 0;
+}
+
+void uv_sleep(unsigned int msec) {
+  Sleep(msec);
+}
diff --git a/wpinet/src/main/native/thirdparty/libuv/src/win/winapi.cpp b/wpinet/src/main/native/thirdparty/libuv/src/win/winapi.cpp
new file mode 100644
index 0000000..53147b8
--- /dev/null
+++ b/wpinet/src/main/native/thirdparty/libuv/src/win/winapi.cpp
@@ -0,0 +1,147 @@
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include <assert.h>
+
+#include "uv.h"
+#include "internal.h"
+
+
+/* Ntdll function pointers */
+sRtlGetVersion pRtlGetVersion;
+sRtlNtStatusToDosError pRtlNtStatusToDosError;
+sNtDeviceIoControlFile pNtDeviceIoControlFile;
+sNtQueryInformationFile pNtQueryInformationFile;
+sNtSetInformationFile pNtSetInformationFile;
+sNtQueryVolumeInformationFile pNtQueryVolumeInformationFile;
+sNtQueryDirectoryFile pNtQueryDirectoryFile;
+sNtQuerySystemInformation pNtQuerySystemInformation;
+sNtQueryInformationProcess pNtQueryInformationProcess;
+
+/* Kernel32 function pointers */
+sGetQueuedCompletionStatusEx pGetQueuedCompletionStatusEx;
+
+/* Powrprof.dll function pointer */
+sPowerRegisterSuspendResumeNotification pPowerRegisterSuspendResumeNotification;
+
+/* User32.dll function pointer */
+sSetWinEventHook pSetWinEventHook;
+
+/* ws2_32.dll function pointer */
+uv_sGetHostNameW pGetHostNameW;
+
+void uv__winapi_init(void) {
+  HMODULE ntdll_module;
+  HMODULE powrprof_module;
+  HMODULE user32_module;
+  HMODULE kernel32_module;
+  HMODULE ws2_32_module;
+
+  ntdll_module = GetModuleHandleA("ntdll.dll");
+  if (ntdll_module == NULL) {
+    uv_fatal_error(GetLastError(), "GetModuleHandleA");
+  }
+
+  pRtlGetVersion = (sRtlGetVersion) GetProcAddress(ntdll_module,
+                                                   "RtlGetVersion");
+
+  pRtlNtStatusToDosError = (sRtlNtStatusToDosError) GetProcAddress(
+      ntdll_module,
+      "RtlNtStatusToDosError");
+  if (pRtlNtStatusToDosError == NULL) {
+    uv_fatal_error(GetLastError(), "GetProcAddress");
+  }
+
+  pNtDeviceIoControlFile = (sNtDeviceIoControlFile) GetProcAddress(
+      ntdll_module,
+      "NtDeviceIoControlFile");
+  if (pNtDeviceIoControlFile == NULL) {
+    uv_fatal_error(GetLastError(), "GetProcAddress");
+  }
+
+  pNtQueryInformationFile = (sNtQueryInformationFile) GetProcAddress(
+      ntdll_module,
+      "NtQueryInformationFile");
+  if (pNtQueryInformationFile == NULL) {
+    uv_fatal_error(GetLastError(), "GetProcAddress");
+  }
+
+  pNtSetInformationFile = (sNtSetInformationFile) GetProcAddress(
+      ntdll_module,
+      "NtSetInformationFile");
+  if (pNtSetInformationFile == NULL) {
+    uv_fatal_error(GetLastError(), "GetProcAddress");
+  }
+
+  pNtQueryVolumeInformationFile = (sNtQueryVolumeInformationFile)
+      GetProcAddress(ntdll_module, "NtQueryVolumeInformationFile");
+  if (pNtQueryVolumeInformationFile == NULL) {
+    uv_fatal_error(GetLastError(), "GetProcAddress");
+  }
+
+  pNtQueryDirectoryFile = (sNtQueryDirectoryFile)
+      GetProcAddress(ntdll_module, "NtQueryDirectoryFile");
+  if (pNtQueryVolumeInformationFile == NULL) {
+    uv_fatal_error(GetLastError(), "GetProcAddress");
+  }
+
+  pNtQuerySystemInformation = (sNtQuerySystemInformation) GetProcAddress(
+      ntdll_module,
+      "NtQuerySystemInformation");
+  if (pNtQuerySystemInformation == NULL) {
+    uv_fatal_error(GetLastError(), "GetProcAddress");
+  }
+
+  pNtQueryInformationProcess = (sNtQueryInformationProcess) GetProcAddress(
+      ntdll_module,
+      "NtQueryInformationProcess");
+  if (pNtQueryInformationProcess == NULL) {
+    uv_fatal_error(GetLastError(), "GetProcAddress");
+  }
+
+  kernel32_module = GetModuleHandleA("kernel32.dll");
+  if (kernel32_module == NULL) {
+    uv_fatal_error(GetLastError(), "GetModuleHandleA");
+  }
+
+  pGetQueuedCompletionStatusEx = (sGetQueuedCompletionStatusEx) GetProcAddress(
+      kernel32_module,
+      "GetQueuedCompletionStatusEx");
+
+  powrprof_module = LoadLibraryExA("powrprof.dll", NULL, LOAD_LIBRARY_SEARCH_SYSTEM32);
+  if (powrprof_module != NULL) {
+    pPowerRegisterSuspendResumeNotification = (sPowerRegisterSuspendResumeNotification)
+      GetProcAddress(powrprof_module, "PowerRegisterSuspendResumeNotification");
+  }
+
+  user32_module = GetModuleHandleA("user32.dll");
+  if (user32_module != NULL) {
+    pSetWinEventHook = (sSetWinEventHook)
+      GetProcAddress(user32_module, "SetWinEventHook");
+  }
+
+  ws2_32_module = GetModuleHandleA("ws2_32.dll");
+  if (ws2_32_module != NULL) {
+    pGetHostNameW = (uv_sGetHostNameW) GetProcAddress(
+        ws2_32_module,
+        "GetHostNameW");
+  }
+}
diff --git a/wpinet/src/main/native/thirdparty/libuv/src/win/winapi.h b/wpinet/src/main/native/thirdparty/libuv/src/win/winapi.h
new file mode 100644
index 0000000..d380bda
--- /dev/null
+++ b/wpinet/src/main/native/thirdparty/libuv/src/win/winapi.h
@@ -0,0 +1,4769 @@
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#ifndef UV_WIN_WINAPI_H_
+#define UV_WIN_WINAPI_H_
+
+#include <windows.h>
+
+
+/*
+ * Ntdll headers
+ */
+#ifndef STATUS_SEVERITY_SUCCESS
+# define STATUS_SEVERITY_SUCCESS 0x0
+#endif
+
+#ifndef STATUS_SEVERITY_INFORMATIONAL
+# define STATUS_SEVERITY_INFORMATIONAL 0x1
+#endif
+
+#ifndef STATUS_SEVERITY_WARNING
+# define STATUS_SEVERITY_WARNING 0x2
+#endif
+
+#ifndef STATUS_SEVERITY_ERROR
+# define STATUS_SEVERITY_ERROR 0x3
+#endif
+
+#ifndef FACILITY_NTWIN32
+# define FACILITY_NTWIN32 0x7
+#endif
+
+#ifndef NT_SUCCESS
+# define NT_SUCCESS(status) (((NTSTATUS) (status)) >= 0)
+#endif
+
+#ifndef NT_INFORMATION
+# define NT_INFORMATION(status) ((((ULONG) (status)) >> 30) == 1)
+#endif
+
+#ifndef NT_WARNING
+# define NT_WARNING(status) ((((ULONG) (status)) >> 30) == 2)
+#endif
+
+#ifndef NT_ERROR
+# define NT_ERROR(status) ((((ULONG) (status)) >> 30) == 3)
+#endif
+
+#ifndef STATUS_SUCCESS
+# define STATUS_SUCCESS ((NTSTATUS) 0x00000000L)
+#endif
+
+#ifndef STATUS_WAIT_0
+# define STATUS_WAIT_0 ((NTSTATUS) 0x00000000L)
+#endif
+
+#ifndef STATUS_WAIT_1
+# define STATUS_WAIT_1 ((NTSTATUS) 0x00000001L)
+#endif
+
+#ifndef STATUS_WAIT_2
+# define STATUS_WAIT_2 ((NTSTATUS) 0x00000002L)
+#endif
+
+#ifndef STATUS_WAIT_3
+# define STATUS_WAIT_3 ((NTSTATUS) 0x00000003L)
+#endif
+
+#ifndef STATUS_WAIT_63
+# define STATUS_WAIT_63 ((NTSTATUS) 0x0000003FL)
+#endif
+
+#ifndef STATUS_ABANDONED
+# define STATUS_ABANDONED ((NTSTATUS) 0x00000080L)
+#endif
+
+#ifndef STATUS_ABANDONED_WAIT_0
+# define STATUS_ABANDONED_WAIT_0 ((NTSTATUS) 0x00000080L)
+#endif
+
+#ifndef STATUS_ABANDONED_WAIT_63
+# define STATUS_ABANDONED_WAIT_63 ((NTSTATUS) 0x000000BFL)
+#endif
+
+#ifndef STATUS_USER_APC
+# define STATUS_USER_APC ((NTSTATUS) 0x000000C0L)
+#endif
+
+#ifndef STATUS_KERNEL_APC
+# define STATUS_KERNEL_APC ((NTSTATUS) 0x00000100L)
+#endif
+
+#ifndef STATUS_ALERTED
+# define STATUS_ALERTED ((NTSTATUS) 0x00000101L)
+#endif
+
+#ifndef STATUS_TIMEOUT
+# define STATUS_TIMEOUT ((NTSTATUS) 0x00000102L)
+#endif
+
+#ifndef STATUS_PENDING
+# define STATUS_PENDING ((NTSTATUS) 0x00000103L)
+#endif
+
+#ifndef STATUS_REPARSE
+# define STATUS_REPARSE ((NTSTATUS) 0x00000104L)
+#endif
+
+#ifndef STATUS_MORE_ENTRIES
+# define STATUS_MORE_ENTRIES ((NTSTATUS) 0x00000105L)
+#endif
+
+#ifndef STATUS_NOT_ALL_ASSIGNED
+# define STATUS_NOT_ALL_ASSIGNED ((NTSTATUS) 0x00000106L)
+#endif
+
+#ifndef STATUS_SOME_NOT_MAPPED
+# define STATUS_SOME_NOT_MAPPED ((NTSTATUS) 0x00000107L)
+#endif
+
+#ifndef STATUS_OPLOCK_BREAK_IN_PROGRESS
+# define STATUS_OPLOCK_BREAK_IN_PROGRESS ((NTSTATUS) 0x00000108L)
+#endif
+
+#ifndef STATUS_VOLUME_MOUNTED
+# define STATUS_VOLUME_MOUNTED ((NTSTATUS) 0x00000109L)
+#endif
+
+#ifndef STATUS_RXACT_COMMITTED
+# define STATUS_RXACT_COMMITTED ((NTSTATUS) 0x0000010AL)
+#endif
+
+#ifndef STATUS_NOTIFY_CLEANUP
+# define STATUS_NOTIFY_CLEANUP ((NTSTATUS) 0x0000010BL)
+#endif
+
+#ifndef STATUS_NOTIFY_ENUM_DIR
+# define STATUS_NOTIFY_ENUM_DIR ((NTSTATUS) 0x0000010CL)
+#endif
+
+#ifndef STATUS_NO_QUOTAS_FOR_ACCOUNT
+# define STATUS_NO_QUOTAS_FOR_ACCOUNT ((NTSTATUS) 0x0000010DL)
+#endif
+
+#ifndef STATUS_PRIMARY_TRANSPORT_CONNECT_FAILED
+# define STATUS_PRIMARY_TRANSPORT_CONNECT_FAILED ((NTSTATUS) 0x0000010EL)
+#endif
+
+#ifndef STATUS_PAGE_FAULT_TRANSITION
+# define STATUS_PAGE_FAULT_TRANSITION ((NTSTATUS) 0x00000110L)
+#endif
+
+#ifndef STATUS_PAGE_FAULT_DEMAND_ZERO
+# define STATUS_PAGE_FAULT_DEMAND_ZERO ((NTSTATUS) 0x00000111L)
+#endif
+
+#ifndef STATUS_PAGE_FAULT_COPY_ON_WRITE
+# define STATUS_PAGE_FAULT_COPY_ON_WRITE ((NTSTATUS) 0x00000112L)
+#endif
+
+#ifndef STATUS_PAGE_FAULT_GUARD_PAGE
+# define STATUS_PAGE_FAULT_GUARD_PAGE ((NTSTATUS) 0x00000113L)
+#endif
+
+#ifndef STATUS_PAGE_FAULT_PAGING_FILE
+# define STATUS_PAGE_FAULT_PAGING_FILE ((NTSTATUS) 0x00000114L)
+#endif
+
+#ifndef STATUS_CACHE_PAGE_LOCKED
+# define STATUS_CACHE_PAGE_LOCKED ((NTSTATUS) 0x00000115L)
+#endif
+
+#ifndef STATUS_CRASH_DUMP
+# define STATUS_CRASH_DUMP ((NTSTATUS) 0x00000116L)
+#endif
+
+#ifndef STATUS_BUFFER_ALL_ZEROS
+# define STATUS_BUFFER_ALL_ZEROS ((NTSTATUS) 0x00000117L)
+#endif
+
+#ifndef STATUS_REPARSE_OBJECT
+# define STATUS_REPARSE_OBJECT ((NTSTATUS) 0x00000118L)
+#endif
+
+#ifndef STATUS_RESOURCE_REQUIREMENTS_CHANGED
+# define STATUS_RESOURCE_REQUIREMENTS_CHANGED ((NTSTATUS) 0x00000119L)
+#endif
+
+#ifndef STATUS_TRANSLATION_COMPLETE
+# define STATUS_TRANSLATION_COMPLETE ((NTSTATUS) 0x00000120L)
+#endif
+
+#ifndef STATUS_DS_MEMBERSHIP_EVALUATED_LOCALLY
+# define STATUS_DS_MEMBERSHIP_EVALUATED_LOCALLY ((NTSTATUS) 0x00000121L)
+#endif
+
+#ifndef STATUS_NOTHING_TO_TERMINATE
+# define STATUS_NOTHING_TO_TERMINATE ((NTSTATUS) 0x00000122L)
+#endif
+
+#ifndef STATUS_PROCESS_NOT_IN_JOB
+# define STATUS_PROCESS_NOT_IN_JOB ((NTSTATUS) 0x00000123L)
+#endif
+
+#ifndef STATUS_PROCESS_IN_JOB
+# define STATUS_PROCESS_IN_JOB ((NTSTATUS) 0x00000124L)
+#endif
+
+#ifndef STATUS_VOLSNAP_HIBERNATE_READY
+# define STATUS_VOLSNAP_HIBERNATE_READY ((NTSTATUS) 0x00000125L)
+#endif
+
+#ifndef STATUS_FSFILTER_OP_COMPLETED_SUCCESSFULLY
+# define STATUS_FSFILTER_OP_COMPLETED_SUCCESSFULLY ((NTSTATUS) 0x00000126L)
+#endif
+
+#ifndef STATUS_INTERRUPT_VECTOR_ALREADY_CONNECTED
+# define STATUS_INTERRUPT_VECTOR_ALREADY_CONNECTED ((NTSTATUS) 0x00000127L)
+#endif
+
+#ifndef STATUS_INTERRUPT_STILL_CONNECTED
+# define STATUS_INTERRUPT_STILL_CONNECTED ((NTSTATUS) 0x00000128L)
+#endif
+
+#ifndef STATUS_PROCESS_CLONED
+# define STATUS_PROCESS_CLONED ((NTSTATUS) 0x00000129L)
+#endif
+
+#ifndef STATUS_FILE_LOCKED_WITH_ONLY_READERS
+# define STATUS_FILE_LOCKED_WITH_ONLY_READERS ((NTSTATUS) 0x0000012AL)
+#endif
+
+#ifndef STATUS_FILE_LOCKED_WITH_WRITERS
+# define STATUS_FILE_LOCKED_WITH_WRITERS ((NTSTATUS) 0x0000012BL)
+#endif
+
+#ifndef STATUS_RESOURCEMANAGER_READ_ONLY
+# define STATUS_RESOURCEMANAGER_READ_ONLY ((NTSTATUS) 0x00000202L)
+#endif
+
+#ifndef STATUS_RING_PREVIOUSLY_EMPTY
+# define STATUS_RING_PREVIOUSLY_EMPTY ((NTSTATUS) 0x00000210L)
+#endif
+
+#ifndef STATUS_RING_PREVIOUSLY_FULL
+# define STATUS_RING_PREVIOUSLY_FULL ((NTSTATUS) 0x00000211L)
+#endif
+
+#ifndef STATUS_RING_PREVIOUSLY_ABOVE_QUOTA
+# define STATUS_RING_PREVIOUSLY_ABOVE_QUOTA ((NTSTATUS) 0x00000212L)
+#endif
+
+#ifndef STATUS_RING_NEWLY_EMPTY
+# define STATUS_RING_NEWLY_EMPTY ((NTSTATUS) 0x00000213L)
+#endif
+
+#ifndef STATUS_RING_SIGNAL_OPPOSITE_ENDPOINT
+# define STATUS_RING_SIGNAL_OPPOSITE_ENDPOINT ((NTSTATUS) 0x00000214L)
+#endif
+
+#ifndef STATUS_OPLOCK_SWITCHED_TO_NEW_HANDLE
+# define STATUS_OPLOCK_SWITCHED_TO_NEW_HANDLE ((NTSTATUS) 0x00000215L)
+#endif
+
+#ifndef STATUS_OPLOCK_HANDLE_CLOSED
+# define STATUS_OPLOCK_HANDLE_CLOSED ((NTSTATUS) 0x00000216L)
+#endif
+
+#ifndef STATUS_WAIT_FOR_OPLOCK
+# define STATUS_WAIT_FOR_OPLOCK ((NTSTATUS) 0x00000367L)
+#endif
+
+#ifndef STATUS_OBJECT_NAME_EXISTS
+# define STATUS_OBJECT_NAME_EXISTS ((NTSTATUS) 0x40000000L)
+#endif
+
+#ifndef STATUS_THREAD_WAS_SUSPENDED
+# define STATUS_THREAD_WAS_SUSPENDED ((NTSTATUS) 0x40000001L)
+#endif
+
+#ifndef STATUS_WORKING_SET_LIMIT_RANGE
+# define STATUS_WORKING_SET_LIMIT_RANGE ((NTSTATUS) 0x40000002L)
+#endif
+
+#ifndef STATUS_IMAGE_NOT_AT_BASE
+# define STATUS_IMAGE_NOT_AT_BASE ((NTSTATUS) 0x40000003L)
+#endif
+
+#ifndef STATUS_RXACT_STATE_CREATED
+# define STATUS_RXACT_STATE_CREATED ((NTSTATUS) 0x40000004L)
+#endif
+
+#ifndef STATUS_SEGMENT_NOTIFICATION
+# define STATUS_SEGMENT_NOTIFICATION ((NTSTATUS) 0x40000005L)
+#endif
+
+#ifndef STATUS_LOCAL_USER_SESSION_KEY
+# define STATUS_LOCAL_USER_SESSION_KEY ((NTSTATUS) 0x40000006L)
+#endif
+
+#ifndef STATUS_BAD_CURRENT_DIRECTORY
+# define STATUS_BAD_CURRENT_DIRECTORY ((NTSTATUS) 0x40000007L)
+#endif
+
+#ifndef STATUS_SERIAL_MORE_WRITES
+# define STATUS_SERIAL_MORE_WRITES ((NTSTATUS) 0x40000008L)
+#endif
+
+#ifndef STATUS_REGISTRY_RECOVERED
+# define STATUS_REGISTRY_RECOVERED ((NTSTATUS) 0x40000009L)
+#endif
+
+#ifndef STATUS_FT_READ_RECOVERY_FROM_BACKUP
+# define STATUS_FT_READ_RECOVERY_FROM_BACKUP ((NTSTATUS) 0x4000000AL)
+#endif
+
+#ifndef STATUS_FT_WRITE_RECOVERY
+# define STATUS_FT_WRITE_RECOVERY ((NTSTATUS) 0x4000000BL)
+#endif
+
+#ifndef STATUS_SERIAL_COUNTER_TIMEOUT
+# define STATUS_SERIAL_COUNTER_TIMEOUT ((NTSTATUS) 0x4000000CL)
+#endif
+
+#ifndef STATUS_NULL_LM_PASSWORD
+# define STATUS_NULL_LM_PASSWORD ((NTSTATUS) 0x4000000DL)
+#endif
+
+#ifndef STATUS_IMAGE_MACHINE_TYPE_MISMATCH
+# define STATUS_IMAGE_MACHINE_TYPE_MISMATCH ((NTSTATUS) 0x4000000EL)
+#endif
+
+#ifndef STATUS_RECEIVE_PARTIAL
+# define STATUS_RECEIVE_PARTIAL ((NTSTATUS) 0x4000000FL)
+#endif
+
+#ifndef STATUS_RECEIVE_EXPEDITED
+# define STATUS_RECEIVE_EXPEDITED ((NTSTATUS) 0x40000010L)
+#endif
+
+#ifndef STATUS_RECEIVE_PARTIAL_EXPEDITED
+# define STATUS_RECEIVE_PARTIAL_EXPEDITED ((NTSTATUS) 0x40000011L)
+#endif
+
+#ifndef STATUS_EVENT_DONE
+# define STATUS_EVENT_DONE ((NTSTATUS) 0x40000012L)
+#endif
+
+#ifndef STATUS_EVENT_PENDING
+# define STATUS_EVENT_PENDING ((NTSTATUS) 0x40000013L)
+#endif
+
+#ifndef STATUS_CHECKING_FILE_SYSTEM
+# define STATUS_CHECKING_FILE_SYSTEM ((NTSTATUS) 0x40000014L)
+#endif
+
+#ifndef STATUS_FATAL_APP_EXIT
+# define STATUS_FATAL_APP_EXIT ((NTSTATUS) 0x40000015L)
+#endif
+
+#ifndef STATUS_PREDEFINED_HANDLE
+# define STATUS_PREDEFINED_HANDLE ((NTSTATUS) 0x40000016L)
+#endif
+
+#ifndef STATUS_WAS_UNLOCKED
+# define STATUS_WAS_UNLOCKED ((NTSTATUS) 0x40000017L)
+#endif
+
+#ifndef STATUS_SERVICE_NOTIFICATION
+# define STATUS_SERVICE_NOTIFICATION ((NTSTATUS) 0x40000018L)
+#endif
+
+#ifndef STATUS_WAS_LOCKED
+# define STATUS_WAS_LOCKED ((NTSTATUS) 0x40000019L)
+#endif
+
+#ifndef STATUS_LOG_HARD_ERROR
+# define STATUS_LOG_HARD_ERROR ((NTSTATUS) 0x4000001AL)
+#endif
+
+#ifndef STATUS_ALREADY_WIN32
+# define STATUS_ALREADY_WIN32 ((NTSTATUS) 0x4000001BL)
+#endif
+
+#ifndef STATUS_WX86_UNSIMULATE
+# define STATUS_WX86_UNSIMULATE ((NTSTATUS) 0x4000001CL)
+#endif
+
+#ifndef STATUS_WX86_CONTINUE
+# define STATUS_WX86_CONTINUE ((NTSTATUS) 0x4000001DL)
+#endif
+
+#ifndef STATUS_WX86_SINGLE_STEP
+# define STATUS_WX86_SINGLE_STEP ((NTSTATUS) 0x4000001EL)
+#endif
+
+#ifndef STATUS_WX86_BREAKPOINT
+# define STATUS_WX86_BREAKPOINT ((NTSTATUS) 0x4000001FL)
+#endif
+
+#ifndef STATUS_WX86_EXCEPTION_CONTINUE
+# define STATUS_WX86_EXCEPTION_CONTINUE ((NTSTATUS) 0x40000020L)
+#endif
+
+#ifndef STATUS_WX86_EXCEPTION_LASTCHANCE
+# define STATUS_WX86_EXCEPTION_LASTCHANCE ((NTSTATUS) 0x40000021L)
+#endif
+
+#ifndef STATUS_WX86_EXCEPTION_CHAIN
+# define STATUS_WX86_EXCEPTION_CHAIN ((NTSTATUS) 0x40000022L)
+#endif
+
+#ifndef STATUS_IMAGE_MACHINE_TYPE_MISMATCH_EXE
+# define STATUS_IMAGE_MACHINE_TYPE_MISMATCH_EXE ((NTSTATUS) 0x40000023L)
+#endif
+
+#ifndef STATUS_NO_YIELD_PERFORMED
+# define STATUS_NO_YIELD_PERFORMED ((NTSTATUS) 0x40000024L)
+#endif
+
+#ifndef STATUS_TIMER_RESUME_IGNORED
+# define STATUS_TIMER_RESUME_IGNORED ((NTSTATUS) 0x40000025L)
+#endif
+
+#ifndef STATUS_ARBITRATION_UNHANDLED
+# define STATUS_ARBITRATION_UNHANDLED ((NTSTATUS) 0x40000026L)
+#endif
+
+#ifndef STATUS_CARDBUS_NOT_SUPPORTED
+# define STATUS_CARDBUS_NOT_SUPPORTED ((NTSTATUS) 0x40000027L)
+#endif
+
+#ifndef STATUS_WX86_CREATEWX86TIB
+# define STATUS_WX86_CREATEWX86TIB ((NTSTATUS) 0x40000028L)
+#endif
+
+#ifndef STATUS_MP_PROCESSOR_MISMATCH
+# define STATUS_MP_PROCESSOR_MISMATCH ((NTSTATUS) 0x40000029L)
+#endif
+
+#ifndef STATUS_HIBERNATED
+# define STATUS_HIBERNATED ((NTSTATUS) 0x4000002AL)
+#endif
+
+#ifndef STATUS_RESUME_HIBERNATION
+# define STATUS_RESUME_HIBERNATION ((NTSTATUS) 0x4000002BL)
+#endif
+
+#ifndef STATUS_FIRMWARE_UPDATED
+# define STATUS_FIRMWARE_UPDATED ((NTSTATUS) 0x4000002CL)
+#endif
+
+#ifndef STATUS_DRIVERS_LEAKING_LOCKED_PAGES
+# define STATUS_DRIVERS_LEAKING_LOCKED_PAGES ((NTSTATUS) 0x4000002DL)
+#endif
+
+#ifndef STATUS_MESSAGE_RETRIEVED
+# define STATUS_MESSAGE_RETRIEVED ((NTSTATUS) 0x4000002EL)
+#endif
+
+#ifndef STATUS_SYSTEM_POWERSTATE_TRANSITION
+# define STATUS_SYSTEM_POWERSTATE_TRANSITION ((NTSTATUS) 0x4000002FL)
+#endif
+
+#ifndef STATUS_ALPC_CHECK_COMPLETION_LIST
+# define STATUS_ALPC_CHECK_COMPLETION_LIST ((NTSTATUS) 0x40000030L)
+#endif
+
+#ifndef STATUS_SYSTEM_POWERSTATE_COMPLEX_TRANSITION
+# define STATUS_SYSTEM_POWERSTATE_COMPLEX_TRANSITION ((NTSTATUS) 0x40000031L)
+#endif
+
+#ifndef STATUS_ACCESS_AUDIT_BY_POLICY
+# define STATUS_ACCESS_AUDIT_BY_POLICY ((NTSTATUS) 0x40000032L)
+#endif
+
+#ifndef STATUS_ABANDON_HIBERFILE
+# define STATUS_ABANDON_HIBERFILE ((NTSTATUS) 0x40000033L)
+#endif
+
+#ifndef STATUS_BIZRULES_NOT_ENABLED
+# define STATUS_BIZRULES_NOT_ENABLED ((NTSTATUS) 0x40000034L)
+#endif
+
+#ifndef STATUS_GUARD_PAGE_VIOLATION
+# define STATUS_GUARD_PAGE_VIOLATION ((NTSTATUS) 0x80000001L)
+#endif
+
+#ifndef STATUS_DATATYPE_MISALIGNMENT
+# define STATUS_DATATYPE_MISALIGNMENT ((NTSTATUS) 0x80000002L)
+#endif
+
+#ifndef STATUS_BREAKPOINT
+# define STATUS_BREAKPOINT ((NTSTATUS) 0x80000003L)
+#endif
+
+#ifndef STATUS_SINGLE_STEP
+# define STATUS_SINGLE_STEP ((NTSTATUS) 0x80000004L)
+#endif
+
+#ifndef STATUS_BUFFER_OVERFLOW
+# define STATUS_BUFFER_OVERFLOW ((NTSTATUS) 0x80000005L)
+#endif
+
+#ifndef STATUS_NO_MORE_FILES
+# define STATUS_NO_MORE_FILES ((NTSTATUS) 0x80000006L)
+#endif
+
+#ifndef STATUS_WAKE_SYSTEM_DEBUGGER
+# define STATUS_WAKE_SYSTEM_DEBUGGER ((NTSTATUS) 0x80000007L)
+#endif
+
+#ifndef STATUS_HANDLES_CLOSED
+# define STATUS_HANDLES_CLOSED ((NTSTATUS) 0x8000000AL)
+#endif
+
+#ifndef STATUS_NO_INHERITANCE
+# define STATUS_NO_INHERITANCE ((NTSTATUS) 0x8000000BL)
+#endif
+
+#ifndef STATUS_GUID_SUBSTITUTION_MADE
+# define STATUS_GUID_SUBSTITUTION_MADE ((NTSTATUS) 0x8000000CL)
+#endif
+
+#ifndef STATUS_PARTIAL_COPY
+# define STATUS_PARTIAL_COPY ((NTSTATUS) 0x8000000DL)
+#endif
+
+#ifndef STATUS_DEVICE_PAPER_EMPTY
+# define STATUS_DEVICE_PAPER_EMPTY ((NTSTATUS) 0x8000000EL)
+#endif
+
+#ifndef STATUS_DEVICE_POWERED_OFF
+# define STATUS_DEVICE_POWERED_OFF ((NTSTATUS) 0x8000000FL)
+#endif
+
+#ifndef STATUS_DEVICE_OFF_LINE
+# define STATUS_DEVICE_OFF_LINE ((NTSTATUS) 0x80000010L)
+#endif
+
+#ifndef STATUS_DEVICE_BUSY
+# define STATUS_DEVICE_BUSY ((NTSTATUS) 0x80000011L)
+#endif
+
+#ifndef STATUS_NO_MORE_EAS
+# define STATUS_NO_MORE_EAS ((NTSTATUS) 0x80000012L)
+#endif
+
+#ifndef STATUS_INVALID_EA_NAME
+# define STATUS_INVALID_EA_NAME ((NTSTATUS) 0x80000013L)
+#endif
+
+#ifndef STATUS_EA_LIST_INCONSISTENT
+# define STATUS_EA_LIST_INCONSISTENT ((NTSTATUS) 0x80000014L)
+#endif
+
+#ifndef STATUS_INVALID_EA_FLAG
+# define STATUS_INVALID_EA_FLAG ((NTSTATUS) 0x80000015L)
+#endif
+
+#ifndef STATUS_VERIFY_REQUIRED
+# define STATUS_VERIFY_REQUIRED ((NTSTATUS) 0x80000016L)
+#endif
+
+#ifndef STATUS_EXTRANEOUS_INFORMATION
+# define STATUS_EXTRANEOUS_INFORMATION ((NTSTATUS) 0x80000017L)
+#endif
+
+#ifndef STATUS_RXACT_COMMIT_NECESSARY
+# define STATUS_RXACT_COMMIT_NECESSARY ((NTSTATUS) 0x80000018L)
+#endif
+
+#ifndef STATUS_NO_MORE_ENTRIES
+# define STATUS_NO_MORE_ENTRIES ((NTSTATUS) 0x8000001AL)
+#endif
+
+#ifndef STATUS_FILEMARK_DETECTED
+# define STATUS_FILEMARK_DETECTED ((NTSTATUS) 0x8000001BL)
+#endif
+
+#ifndef STATUS_MEDIA_CHANGED
+# define STATUS_MEDIA_CHANGED ((NTSTATUS) 0x8000001CL)
+#endif
+
+#ifndef STATUS_BUS_RESET
+# define STATUS_BUS_RESET ((NTSTATUS) 0x8000001DL)
+#endif
+
+#ifndef STATUS_END_OF_MEDIA
+# define STATUS_END_OF_MEDIA ((NTSTATUS) 0x8000001EL)
+#endif
+
+#ifndef STATUS_BEGINNING_OF_MEDIA
+# define STATUS_BEGINNING_OF_MEDIA ((NTSTATUS) 0x8000001FL)
+#endif
+
+#ifndef STATUS_MEDIA_CHECK
+# define STATUS_MEDIA_CHECK ((NTSTATUS) 0x80000020L)
+#endif
+
+#ifndef STATUS_SETMARK_DETECTED
+# define STATUS_SETMARK_DETECTED ((NTSTATUS) 0x80000021L)
+#endif
+
+#ifndef STATUS_NO_DATA_DETECTED
+# define STATUS_NO_DATA_DETECTED ((NTSTATUS) 0x80000022L)
+#endif
+
+#ifndef STATUS_REDIRECTOR_HAS_OPEN_HANDLES
+# define STATUS_REDIRECTOR_HAS_OPEN_HANDLES ((NTSTATUS) 0x80000023L)
+#endif
+
+#ifndef STATUS_SERVER_HAS_OPEN_HANDLES
+# define STATUS_SERVER_HAS_OPEN_HANDLES ((NTSTATUS) 0x80000024L)
+#endif
+
+#ifndef STATUS_ALREADY_DISCONNECTED
+# define STATUS_ALREADY_DISCONNECTED ((NTSTATUS) 0x80000025L)
+#endif
+
+#ifndef STATUS_LONGJUMP
+# define STATUS_LONGJUMP ((NTSTATUS) 0x80000026L)
+#endif
+
+#ifndef STATUS_CLEANER_CARTRIDGE_INSTALLED
+# define STATUS_CLEANER_CARTRIDGE_INSTALLED ((NTSTATUS) 0x80000027L)
+#endif
+
+#ifndef STATUS_PLUGPLAY_QUERY_VETOED
+# define STATUS_PLUGPLAY_QUERY_VETOED ((NTSTATUS) 0x80000028L)
+#endif
+
+#ifndef STATUS_UNWIND_CONSOLIDATE
+# define STATUS_UNWIND_CONSOLIDATE ((NTSTATUS) 0x80000029L)
+#endif
+
+#ifndef STATUS_REGISTRY_HIVE_RECOVERED
+# define STATUS_REGISTRY_HIVE_RECOVERED ((NTSTATUS) 0x8000002AL)
+#endif
+
+#ifndef STATUS_DLL_MIGHT_BE_INSECURE
+# define STATUS_DLL_MIGHT_BE_INSECURE ((NTSTATUS) 0x8000002BL)
+#endif
+
+#ifndef STATUS_DLL_MIGHT_BE_INCOMPATIBLE
+# define STATUS_DLL_MIGHT_BE_INCOMPATIBLE ((NTSTATUS) 0x8000002CL)
+#endif
+
+#ifndef STATUS_STOPPED_ON_SYMLINK
+# define STATUS_STOPPED_ON_SYMLINK ((NTSTATUS) 0x8000002DL)
+#endif
+
+#ifndef STATUS_CANNOT_GRANT_REQUESTED_OPLOCK
+# define STATUS_CANNOT_GRANT_REQUESTED_OPLOCK ((NTSTATUS) 0x8000002EL)
+#endif
+
+#ifndef STATUS_NO_ACE_CONDITION
+# define STATUS_NO_ACE_CONDITION ((NTSTATUS) 0x8000002FL)
+#endif
+
+#ifndef STATUS_UNSUCCESSFUL
+# define STATUS_UNSUCCESSFUL ((NTSTATUS) 0xC0000001L)
+#endif
+
+#ifndef STATUS_NOT_IMPLEMENTED
+# define STATUS_NOT_IMPLEMENTED ((NTSTATUS) 0xC0000002L)
+#endif
+
+#ifndef STATUS_INVALID_INFO_CLASS
+# define STATUS_INVALID_INFO_CLASS ((NTSTATUS) 0xC0000003L)
+#endif
+
+#ifndef STATUS_INFO_LENGTH_MISMATCH
+# define STATUS_INFO_LENGTH_MISMATCH ((NTSTATUS) 0xC0000004L)
+#endif
+
+#ifndef STATUS_ACCESS_VIOLATION
+# define STATUS_ACCESS_VIOLATION ((NTSTATUS) 0xC0000005L)
+#endif
+
+#ifndef STATUS_IN_PAGE_ERROR
+# define STATUS_IN_PAGE_ERROR ((NTSTATUS) 0xC0000006L)
+#endif
+
+#ifndef STATUS_PAGEFILE_QUOTA
+# define STATUS_PAGEFILE_QUOTA ((NTSTATUS) 0xC0000007L)
+#endif
+
+#ifndef STATUS_INVALID_HANDLE
+# define STATUS_INVALID_HANDLE ((NTSTATUS) 0xC0000008L)
+#endif
+
+#ifndef STATUS_BAD_INITIAL_STACK
+# define STATUS_BAD_INITIAL_STACK ((NTSTATUS) 0xC0000009L)
+#endif
+
+#ifndef STATUS_BAD_INITIAL_PC
+# define STATUS_BAD_INITIAL_PC ((NTSTATUS) 0xC000000AL)
+#endif
+
+#ifndef STATUS_INVALID_CID
+# define STATUS_INVALID_CID ((NTSTATUS) 0xC000000BL)
+#endif
+
+#ifndef STATUS_TIMER_NOT_CANCELED
+# define STATUS_TIMER_NOT_CANCELED ((NTSTATUS) 0xC000000CL)
+#endif
+
+#ifndef STATUS_INVALID_PARAMETER
+# define STATUS_INVALID_PARAMETER ((NTSTATUS) 0xC000000DL)
+#endif
+
+#ifndef STATUS_NO_SUCH_DEVICE
+# define STATUS_NO_SUCH_DEVICE ((NTSTATUS) 0xC000000EL)
+#endif
+
+#ifndef STATUS_NO_SUCH_FILE
+# define STATUS_NO_SUCH_FILE ((NTSTATUS) 0xC000000FL)
+#endif
+
+#ifndef STATUS_INVALID_DEVICE_REQUEST
+# define STATUS_INVALID_DEVICE_REQUEST ((NTSTATUS) 0xC0000010L)
+#endif
+
+#ifndef STATUS_END_OF_FILE
+# define STATUS_END_OF_FILE ((NTSTATUS) 0xC0000011L)
+#endif
+
+#ifndef STATUS_WRONG_VOLUME
+# define STATUS_WRONG_VOLUME ((NTSTATUS) 0xC0000012L)
+#endif
+
+#ifndef STATUS_NO_MEDIA_IN_DEVICE
+# define STATUS_NO_MEDIA_IN_DEVICE ((NTSTATUS) 0xC0000013L)
+#endif
+
+#ifndef STATUS_UNRECOGNIZED_MEDIA
+# define STATUS_UNRECOGNIZED_MEDIA ((NTSTATUS) 0xC0000014L)
+#endif
+
+#ifndef STATUS_NONEXISTENT_SECTOR
+# define STATUS_NONEXISTENT_SECTOR ((NTSTATUS) 0xC0000015L)
+#endif
+
+#ifndef STATUS_MORE_PROCESSING_REQUIRED
+# define STATUS_MORE_PROCESSING_REQUIRED ((NTSTATUS) 0xC0000016L)
+#endif
+
+#ifndef STATUS_NO_MEMORY
+# define STATUS_NO_MEMORY ((NTSTATUS) 0xC0000017L)
+#endif
+
+#ifndef STATUS_CONFLICTING_ADDRESSES
+# define STATUS_CONFLICTING_ADDRESSES ((NTSTATUS) 0xC0000018L)
+#endif
+
+#ifndef STATUS_NOT_MAPPED_VIEW
+# define STATUS_NOT_MAPPED_VIEW ((NTSTATUS) 0xC0000019L)
+#endif
+
+#ifndef STATUS_UNABLE_TO_FREE_VM
+# define STATUS_UNABLE_TO_FREE_VM ((NTSTATUS) 0xC000001AL)
+#endif
+
+#ifndef STATUS_UNABLE_TO_DELETE_SECTION
+# define STATUS_UNABLE_TO_DELETE_SECTION ((NTSTATUS) 0xC000001BL)
+#endif
+
+#ifndef STATUS_INVALID_SYSTEM_SERVICE
+# define STATUS_INVALID_SYSTEM_SERVICE ((NTSTATUS) 0xC000001CL)
+#endif
+
+#ifndef STATUS_ILLEGAL_INSTRUCTION
+# define STATUS_ILLEGAL_INSTRUCTION ((NTSTATUS) 0xC000001DL)
+#endif
+
+#ifndef STATUS_INVALID_LOCK_SEQUENCE
+# define STATUS_INVALID_LOCK_SEQUENCE ((NTSTATUS) 0xC000001EL)
+#endif
+
+#ifndef STATUS_INVALID_VIEW_SIZE
+# define STATUS_INVALID_VIEW_SIZE ((NTSTATUS) 0xC000001FL)
+#endif
+
+#ifndef STATUS_INVALID_FILE_FOR_SECTION
+# define STATUS_INVALID_FILE_FOR_SECTION ((NTSTATUS) 0xC0000020L)
+#endif
+
+#ifndef STATUS_ALREADY_COMMITTED
+# define STATUS_ALREADY_COMMITTED ((NTSTATUS) 0xC0000021L)
+#endif
+
+#ifndef STATUS_ACCESS_DENIED
+# define STATUS_ACCESS_DENIED ((NTSTATUS) 0xC0000022L)
+#endif
+
+#ifndef STATUS_BUFFER_TOO_SMALL
+# define STATUS_BUFFER_TOO_SMALL ((NTSTATUS) 0xC0000023L)
+#endif
+
+#ifndef STATUS_OBJECT_TYPE_MISMATCH
+# define STATUS_OBJECT_TYPE_MISMATCH ((NTSTATUS) 0xC0000024L)
+#endif
+
+#ifndef STATUS_NONCONTINUABLE_EXCEPTION
+# define STATUS_NONCONTINUABLE_EXCEPTION ((NTSTATUS) 0xC0000025L)
+#endif
+
+#ifndef STATUS_INVALID_DISPOSITION
+# define STATUS_INVALID_DISPOSITION ((NTSTATUS) 0xC0000026L)
+#endif
+
+#ifndef STATUS_UNWIND
+# define STATUS_UNWIND ((NTSTATUS) 0xC0000027L)
+#endif
+
+#ifndef STATUS_BAD_STACK
+# define STATUS_BAD_STACK ((NTSTATUS) 0xC0000028L)
+#endif
+
+#ifndef STATUS_INVALID_UNWIND_TARGET
+# define STATUS_INVALID_UNWIND_TARGET ((NTSTATUS) 0xC0000029L)
+#endif
+
+#ifndef STATUS_NOT_LOCKED
+# define STATUS_NOT_LOCKED ((NTSTATUS) 0xC000002AL)
+#endif
+
+#ifndef STATUS_PARITY_ERROR
+# define STATUS_PARITY_ERROR ((NTSTATUS) 0xC000002BL)
+#endif
+
+#ifndef STATUS_UNABLE_TO_DECOMMIT_VM
+# define STATUS_UNABLE_TO_DECOMMIT_VM ((NTSTATUS) 0xC000002CL)
+#endif
+
+#ifndef STATUS_NOT_COMMITTED
+# define STATUS_NOT_COMMITTED ((NTSTATUS) 0xC000002DL)
+#endif
+
+#ifndef STATUS_INVALID_PORT_ATTRIBUTES
+# define STATUS_INVALID_PORT_ATTRIBUTES ((NTSTATUS) 0xC000002EL)
+#endif
+
+#ifndef STATUS_PORT_MESSAGE_TOO_LONG
+# define STATUS_PORT_MESSAGE_TOO_LONG ((NTSTATUS) 0xC000002FL)
+#endif
+
+#ifndef STATUS_INVALID_PARAMETER_MIX
+# define STATUS_INVALID_PARAMETER_MIX ((NTSTATUS) 0xC0000030L)
+#endif
+
+#ifndef STATUS_INVALID_QUOTA_LOWER
+# define STATUS_INVALID_QUOTA_LOWER ((NTSTATUS) 0xC0000031L)
+#endif
+
+#ifndef STATUS_DISK_CORRUPT_ERROR
+# define STATUS_DISK_CORRUPT_ERROR ((NTSTATUS) 0xC0000032L)
+#endif
+
+#ifndef STATUS_OBJECT_NAME_INVALID
+# define STATUS_OBJECT_NAME_INVALID ((NTSTATUS) 0xC0000033L)
+#endif
+
+#ifndef STATUS_OBJECT_NAME_NOT_FOUND
+# define STATUS_OBJECT_NAME_NOT_FOUND ((NTSTATUS) 0xC0000034L)
+#endif
+
+#ifndef STATUS_OBJECT_NAME_COLLISION
+# define STATUS_OBJECT_NAME_COLLISION ((NTSTATUS) 0xC0000035L)
+#endif
+
+#ifndef STATUS_PORT_DISCONNECTED
+# define STATUS_PORT_DISCONNECTED ((NTSTATUS) 0xC0000037L)
+#endif
+
+#ifndef STATUS_DEVICE_ALREADY_ATTACHED
+# define STATUS_DEVICE_ALREADY_ATTACHED ((NTSTATUS) 0xC0000038L)
+#endif
+
+#ifndef STATUS_OBJECT_PATH_INVALID
+# define STATUS_OBJECT_PATH_INVALID ((NTSTATUS) 0xC0000039L)
+#endif
+
+#ifndef STATUS_OBJECT_PATH_NOT_FOUND
+# define STATUS_OBJECT_PATH_NOT_FOUND ((NTSTATUS) 0xC000003AL)
+#endif
+
+#ifndef STATUS_OBJECT_PATH_SYNTAX_BAD
+# define STATUS_OBJECT_PATH_SYNTAX_BAD ((NTSTATUS) 0xC000003BL)
+#endif
+
+#ifndef STATUS_DATA_OVERRUN
+# define STATUS_DATA_OVERRUN ((NTSTATUS) 0xC000003CL)
+#endif
+
+#ifndef STATUS_DATA_LATE_ERROR
+# define STATUS_DATA_LATE_ERROR ((NTSTATUS) 0xC000003DL)
+#endif
+
+#ifndef STATUS_DATA_ERROR
+# define STATUS_DATA_ERROR ((NTSTATUS) 0xC000003EL)
+#endif
+
+#ifndef STATUS_CRC_ERROR
+# define STATUS_CRC_ERROR ((NTSTATUS) 0xC000003FL)
+#endif
+
+#ifndef STATUS_SECTION_TOO_BIG
+# define STATUS_SECTION_TOO_BIG ((NTSTATUS) 0xC0000040L)
+#endif
+
+#ifndef STATUS_PORT_CONNECTION_REFUSED
+# define STATUS_PORT_CONNECTION_REFUSED ((NTSTATUS) 0xC0000041L)
+#endif
+
+#ifndef STATUS_INVALID_PORT_HANDLE
+# define STATUS_INVALID_PORT_HANDLE ((NTSTATUS) 0xC0000042L)
+#endif
+
+#ifndef STATUS_SHARING_VIOLATION
+# define STATUS_SHARING_VIOLATION ((NTSTATUS) 0xC0000043L)
+#endif
+
+#ifndef STATUS_QUOTA_EXCEEDED
+# define STATUS_QUOTA_EXCEEDED ((NTSTATUS) 0xC0000044L)
+#endif
+
+#ifndef STATUS_INVALID_PAGE_PROTECTION
+# define STATUS_INVALID_PAGE_PROTECTION ((NTSTATUS) 0xC0000045L)
+#endif
+
+#ifndef STATUS_MUTANT_NOT_OWNED
+# define STATUS_MUTANT_NOT_OWNED ((NTSTATUS) 0xC0000046L)
+#endif
+
+#ifndef STATUS_SEMAPHORE_LIMIT_EXCEEDED
+# define STATUS_SEMAPHORE_LIMIT_EXCEEDED ((NTSTATUS) 0xC0000047L)
+#endif
+
+#ifndef STATUS_PORT_ALREADY_SET
+# define STATUS_PORT_ALREADY_SET ((NTSTATUS) 0xC0000048L)
+#endif
+
+#ifndef STATUS_SECTION_NOT_IMAGE
+# define STATUS_SECTION_NOT_IMAGE ((NTSTATUS) 0xC0000049L)
+#endif
+
+#ifndef STATUS_SUSPEND_COUNT_EXCEEDED
+# define STATUS_SUSPEND_COUNT_EXCEEDED ((NTSTATUS) 0xC000004AL)
+#endif
+
+#ifndef STATUS_THREAD_IS_TERMINATING
+# define STATUS_THREAD_IS_TERMINATING ((NTSTATUS) 0xC000004BL)
+#endif
+
+#ifndef STATUS_BAD_WORKING_SET_LIMIT
+# define STATUS_BAD_WORKING_SET_LIMIT ((NTSTATUS) 0xC000004CL)
+#endif
+
+#ifndef STATUS_INCOMPATIBLE_FILE_MAP
+# define STATUS_INCOMPATIBLE_FILE_MAP ((NTSTATUS) 0xC000004DL)
+#endif
+
+#ifndef STATUS_SECTION_PROTECTION
+# define STATUS_SECTION_PROTECTION ((NTSTATUS) 0xC000004EL)
+#endif
+
+#ifndef STATUS_EAS_NOT_SUPPORTED
+# define STATUS_EAS_NOT_SUPPORTED ((NTSTATUS) 0xC000004FL)
+#endif
+
+#ifndef STATUS_EA_TOO_LARGE
+# define STATUS_EA_TOO_LARGE ((NTSTATUS) 0xC0000050L)
+#endif
+
+#ifndef STATUS_NONEXISTENT_EA_ENTRY
+# define STATUS_NONEXISTENT_EA_ENTRY ((NTSTATUS) 0xC0000051L)
+#endif
+
+#ifndef STATUS_NO_EAS_ON_FILE
+# define STATUS_NO_EAS_ON_FILE ((NTSTATUS) 0xC0000052L)
+#endif
+
+#ifndef STATUS_EA_CORRUPT_ERROR
+# define STATUS_EA_CORRUPT_ERROR ((NTSTATUS) 0xC0000053L)
+#endif
+
+#ifndef STATUS_FILE_LOCK_CONFLICT
+# define STATUS_FILE_LOCK_CONFLICT ((NTSTATUS) 0xC0000054L)
+#endif
+
+#ifndef STATUS_LOCK_NOT_GRANTED
+# define STATUS_LOCK_NOT_GRANTED ((NTSTATUS) 0xC0000055L)
+#endif
+
+#ifndef STATUS_DELETE_PENDING
+# define STATUS_DELETE_PENDING ((NTSTATUS) 0xC0000056L)
+#endif
+
+#ifndef STATUS_CTL_FILE_NOT_SUPPORTED
+# define STATUS_CTL_FILE_NOT_SUPPORTED ((NTSTATUS) 0xC0000057L)
+#endif
+
+#ifndef STATUS_UNKNOWN_REVISION
+# define STATUS_UNKNOWN_REVISION ((NTSTATUS) 0xC0000058L)
+#endif
+
+#ifndef STATUS_REVISION_MISMATCH
+# define STATUS_REVISION_MISMATCH ((NTSTATUS) 0xC0000059L)
+#endif
+
+#ifndef STATUS_INVALID_OWNER
+# define STATUS_INVALID_OWNER ((NTSTATUS) 0xC000005AL)
+#endif
+
+#ifndef STATUS_INVALID_PRIMARY_GROUP
+# define STATUS_INVALID_PRIMARY_GROUP ((NTSTATUS) 0xC000005BL)
+#endif
+
+#ifndef STATUS_NO_IMPERSONATION_TOKEN
+# define STATUS_NO_IMPERSONATION_TOKEN ((NTSTATUS) 0xC000005CL)
+#endif
+
+#ifndef STATUS_CANT_DISABLE_MANDATORY
+# define STATUS_CANT_DISABLE_MANDATORY ((NTSTATUS) 0xC000005DL)
+#endif
+
+#ifndef STATUS_NO_LOGON_SERVERS
+# define STATUS_NO_LOGON_SERVERS ((NTSTATUS) 0xC000005EL)
+#endif
+
+#ifndef STATUS_NO_SUCH_LOGON_SESSION
+# define STATUS_NO_SUCH_LOGON_SESSION ((NTSTATUS) 0xC000005FL)
+#endif
+
+#ifndef STATUS_NO_SUCH_PRIVILEGE
+# define STATUS_NO_SUCH_PRIVILEGE ((NTSTATUS) 0xC0000060L)
+#endif
+
+#ifndef STATUS_PRIVILEGE_NOT_HELD
+# define STATUS_PRIVILEGE_NOT_HELD ((NTSTATUS) 0xC0000061L)
+#endif
+
+#ifndef STATUS_INVALID_ACCOUNT_NAME
+# define STATUS_INVALID_ACCOUNT_NAME ((NTSTATUS) 0xC0000062L)
+#endif
+
+#ifndef STATUS_USER_EXISTS
+# define STATUS_USER_EXISTS ((NTSTATUS) 0xC0000063L)
+#endif
+
+#ifndef STATUS_NO_SUCH_USER
+# define STATUS_NO_SUCH_USER ((NTSTATUS) 0xC0000064L)
+#endif
+
+#ifndef STATUS_GROUP_EXISTS
+# define STATUS_GROUP_EXISTS ((NTSTATUS) 0xC0000065L)
+#endif
+
+#ifndef STATUS_NO_SUCH_GROUP
+# define STATUS_NO_SUCH_GROUP ((NTSTATUS) 0xC0000066L)
+#endif
+
+#ifndef STATUS_MEMBER_IN_GROUP
+# define STATUS_MEMBER_IN_GROUP ((NTSTATUS) 0xC0000067L)
+#endif
+
+#ifndef STATUS_MEMBER_NOT_IN_GROUP
+# define STATUS_MEMBER_NOT_IN_GROUP ((NTSTATUS) 0xC0000068L)
+#endif
+
+#ifndef STATUS_LAST_ADMIN
+# define STATUS_LAST_ADMIN ((NTSTATUS) 0xC0000069L)
+#endif
+
+#ifndef STATUS_WRONG_PASSWORD
+# define STATUS_WRONG_PASSWORD ((NTSTATUS) 0xC000006AL)
+#endif
+
+#ifndef STATUS_ILL_FORMED_PASSWORD
+# define STATUS_ILL_FORMED_PASSWORD ((NTSTATUS) 0xC000006BL)
+#endif
+
+#ifndef STATUS_PASSWORD_RESTRICTION
+# define STATUS_PASSWORD_RESTRICTION ((NTSTATUS) 0xC000006CL)
+#endif
+
+#ifndef STATUS_LOGON_FAILURE
+# define STATUS_LOGON_FAILURE ((NTSTATUS) 0xC000006DL)
+#endif
+
+#ifndef STATUS_ACCOUNT_RESTRICTION
+# define STATUS_ACCOUNT_RESTRICTION ((NTSTATUS) 0xC000006EL)
+#endif
+
+#ifndef STATUS_INVALID_LOGON_HOURS
+# define STATUS_INVALID_LOGON_HOURS ((NTSTATUS) 0xC000006FL)
+#endif
+
+#ifndef STATUS_INVALID_WORKSTATION
+# define STATUS_INVALID_WORKSTATION ((NTSTATUS) 0xC0000070L)
+#endif
+
+#ifndef STATUS_PASSWORD_EXPIRED
+# define STATUS_PASSWORD_EXPIRED ((NTSTATUS) 0xC0000071L)
+#endif
+
+#ifndef STATUS_ACCOUNT_DISABLED
+# define STATUS_ACCOUNT_DISABLED ((NTSTATUS) 0xC0000072L)
+#endif
+
+#ifndef STATUS_NONE_MAPPED
+# define STATUS_NONE_MAPPED ((NTSTATUS) 0xC0000073L)
+#endif
+
+#ifndef STATUS_TOO_MANY_LUIDS_REQUESTED
+# define STATUS_TOO_MANY_LUIDS_REQUESTED ((NTSTATUS) 0xC0000074L)
+#endif
+
+#ifndef STATUS_LUIDS_EXHAUSTED
+# define STATUS_LUIDS_EXHAUSTED ((NTSTATUS) 0xC0000075L)
+#endif
+
+#ifndef STATUS_INVALID_SUB_AUTHORITY
+# define STATUS_INVALID_SUB_AUTHORITY ((NTSTATUS) 0xC0000076L)
+#endif
+
+#ifndef STATUS_INVALID_ACL
+# define STATUS_INVALID_ACL ((NTSTATUS) 0xC0000077L)
+#endif
+
+#ifndef STATUS_INVALID_SID
+# define STATUS_INVALID_SID ((NTSTATUS) 0xC0000078L)
+#endif
+
+#ifndef STATUS_INVALID_SECURITY_DESCR
+# define STATUS_INVALID_SECURITY_DESCR ((NTSTATUS) 0xC0000079L)
+#endif
+
+#ifndef STATUS_PROCEDURE_NOT_FOUND
+# define STATUS_PROCEDURE_NOT_FOUND ((NTSTATUS) 0xC000007AL)
+#endif
+
+#ifndef STATUS_INVALID_IMAGE_FORMAT
+# define STATUS_INVALID_IMAGE_FORMAT ((NTSTATUS) 0xC000007BL)
+#endif
+
+#ifndef STATUS_NO_TOKEN
+# define STATUS_NO_TOKEN ((NTSTATUS) 0xC000007CL)
+#endif
+
+#ifndef STATUS_BAD_INHERITANCE_ACL
+# define STATUS_BAD_INHERITANCE_ACL ((NTSTATUS) 0xC000007DL)
+#endif
+
+#ifndef STATUS_RANGE_NOT_LOCKED
+# define STATUS_RANGE_NOT_LOCKED ((NTSTATUS) 0xC000007EL)
+#endif
+
+#ifndef STATUS_DISK_FULL
+# define STATUS_DISK_FULL ((NTSTATUS) 0xC000007FL)
+#endif
+
+#ifndef STATUS_SERVER_DISABLED
+# define STATUS_SERVER_DISABLED ((NTSTATUS) 0xC0000080L)
+#endif
+
+#ifndef STATUS_SERVER_NOT_DISABLED
+# define STATUS_SERVER_NOT_DISABLED ((NTSTATUS) 0xC0000081L)
+#endif
+
+#ifndef STATUS_TOO_MANY_GUIDS_REQUESTED
+# define STATUS_TOO_MANY_GUIDS_REQUESTED ((NTSTATUS) 0xC0000082L)
+#endif
+
+#ifndef STATUS_GUIDS_EXHAUSTED
+# define STATUS_GUIDS_EXHAUSTED ((NTSTATUS) 0xC0000083L)
+#endif
+
+#ifndef STATUS_INVALID_ID_AUTHORITY
+# define STATUS_INVALID_ID_AUTHORITY ((NTSTATUS) 0xC0000084L)
+#endif
+
+#ifndef STATUS_AGENTS_EXHAUSTED
+# define STATUS_AGENTS_EXHAUSTED ((NTSTATUS) 0xC0000085L)
+#endif
+
+#ifndef STATUS_INVALID_VOLUME_LABEL
+# define STATUS_INVALID_VOLUME_LABEL ((NTSTATUS) 0xC0000086L)
+#endif
+
+#ifndef STATUS_SECTION_NOT_EXTENDED
+# define STATUS_SECTION_NOT_EXTENDED ((NTSTATUS) 0xC0000087L)
+#endif
+
+#ifndef STATUS_NOT_MAPPED_DATA
+# define STATUS_NOT_MAPPED_DATA ((NTSTATUS) 0xC0000088L)
+#endif
+
+#ifndef STATUS_RESOURCE_DATA_NOT_FOUND
+# define STATUS_RESOURCE_DATA_NOT_FOUND ((NTSTATUS) 0xC0000089L)
+#endif
+
+#ifndef STATUS_RESOURCE_TYPE_NOT_FOUND
+# define STATUS_RESOURCE_TYPE_NOT_FOUND ((NTSTATUS) 0xC000008AL)
+#endif
+
+#ifndef STATUS_RESOURCE_NAME_NOT_FOUND
+# define STATUS_RESOURCE_NAME_NOT_FOUND ((NTSTATUS) 0xC000008BL)
+#endif
+
+#ifndef STATUS_ARRAY_BOUNDS_EXCEEDED
+# define STATUS_ARRAY_BOUNDS_EXCEEDED ((NTSTATUS) 0xC000008CL)
+#endif
+
+#ifndef STATUS_FLOAT_DENORMAL_OPERAND
+# define STATUS_FLOAT_DENORMAL_OPERAND ((NTSTATUS) 0xC000008DL)
+#endif
+
+#ifndef STATUS_FLOAT_DIVIDE_BY_ZERO
+# define STATUS_FLOAT_DIVIDE_BY_ZERO ((NTSTATUS) 0xC000008EL)
+#endif
+
+#ifndef STATUS_FLOAT_INEXACT_RESULT
+# define STATUS_FLOAT_INEXACT_RESULT ((NTSTATUS) 0xC000008FL)
+#endif
+
+#ifndef STATUS_FLOAT_INVALID_OPERATION
+# define STATUS_FLOAT_INVALID_OPERATION ((NTSTATUS) 0xC0000090L)
+#endif
+
+#ifndef STATUS_FLOAT_OVERFLOW
+# define STATUS_FLOAT_OVERFLOW ((NTSTATUS) 0xC0000091L)
+#endif
+
+#ifndef STATUS_FLOAT_STACK_CHECK
+# define STATUS_FLOAT_STACK_CHECK ((NTSTATUS) 0xC0000092L)
+#endif
+
+#ifndef STATUS_FLOAT_UNDERFLOW
+# define STATUS_FLOAT_UNDERFLOW ((NTSTATUS) 0xC0000093L)
+#endif
+
+#ifndef STATUS_INTEGER_DIVIDE_BY_ZERO
+# define STATUS_INTEGER_DIVIDE_BY_ZERO ((NTSTATUS) 0xC0000094L)
+#endif
+
+#ifndef STATUS_INTEGER_OVERFLOW
+# define STATUS_INTEGER_OVERFLOW ((NTSTATUS) 0xC0000095L)
+#endif
+
+#ifndef STATUS_PRIVILEGED_INSTRUCTION
+# define STATUS_PRIVILEGED_INSTRUCTION ((NTSTATUS) 0xC0000096L)
+#endif
+
+#ifndef STATUS_TOO_MANY_PAGING_FILES
+# define STATUS_TOO_MANY_PAGING_FILES ((NTSTATUS) 0xC0000097L)
+#endif
+
+#ifndef STATUS_FILE_INVALID
+# define STATUS_FILE_INVALID ((NTSTATUS) 0xC0000098L)
+#endif
+
+#ifndef STATUS_ALLOTTED_SPACE_EXCEEDED
+# define STATUS_ALLOTTED_SPACE_EXCEEDED ((NTSTATUS) 0xC0000099L)
+#endif
+
+#ifndef STATUS_INSUFFICIENT_RESOURCES
+# define STATUS_INSUFFICIENT_RESOURCES ((NTSTATUS) 0xC000009AL)
+#endif
+
+#ifndef STATUS_DFS_EXIT_PATH_FOUND
+# define STATUS_DFS_EXIT_PATH_FOUND ((NTSTATUS) 0xC000009BL)
+#endif
+
+#ifndef STATUS_DEVICE_DATA_ERROR
+# define STATUS_DEVICE_DATA_ERROR ((NTSTATUS) 0xC000009CL)
+#endif
+
+#ifndef STATUS_DEVICE_NOT_CONNECTED
+# define STATUS_DEVICE_NOT_CONNECTED ((NTSTATUS) 0xC000009DL)
+#endif
+
+#ifndef STATUS_DEVICE_POWER_FAILURE
+# define STATUS_DEVICE_POWER_FAILURE ((NTSTATUS) 0xC000009EL)
+#endif
+
+#ifndef STATUS_FREE_VM_NOT_AT_BASE
+# define STATUS_FREE_VM_NOT_AT_BASE ((NTSTATUS) 0xC000009FL)
+#endif
+
+#ifndef STATUS_MEMORY_NOT_ALLOCATED
+# define STATUS_MEMORY_NOT_ALLOCATED ((NTSTATUS) 0xC00000A0L)
+#endif
+
+#ifndef STATUS_WORKING_SET_QUOTA
+# define STATUS_WORKING_SET_QUOTA ((NTSTATUS) 0xC00000A1L)
+#endif
+
+#ifndef STATUS_MEDIA_WRITE_PROTECTED
+# define STATUS_MEDIA_WRITE_PROTECTED ((NTSTATUS) 0xC00000A2L)
+#endif
+
+#ifndef STATUS_DEVICE_NOT_READY
+# define STATUS_DEVICE_NOT_READY ((NTSTATUS) 0xC00000A3L)
+#endif
+
+#ifndef STATUS_INVALID_GROUP_ATTRIBUTES
+# define STATUS_INVALID_GROUP_ATTRIBUTES ((NTSTATUS) 0xC00000A4L)
+#endif
+
+#ifndef STATUS_BAD_IMPERSONATION_LEVEL
+# define STATUS_BAD_IMPERSONATION_LEVEL ((NTSTATUS) 0xC00000A5L)
+#endif
+
+#ifndef STATUS_CANT_OPEN_ANONYMOUS
+# define STATUS_CANT_OPEN_ANONYMOUS ((NTSTATUS) 0xC00000A6L)
+#endif
+
+#ifndef STATUS_BAD_VALIDATION_CLASS
+# define STATUS_BAD_VALIDATION_CLASS ((NTSTATUS) 0xC00000A7L)
+#endif
+
+#ifndef STATUS_BAD_TOKEN_TYPE
+# define STATUS_BAD_TOKEN_TYPE ((NTSTATUS) 0xC00000A8L)
+#endif
+
+#ifndef STATUS_BAD_MASTER_BOOT_RECORD
+# define STATUS_BAD_MASTER_BOOT_RECORD ((NTSTATUS) 0xC00000A9L)
+#endif
+
+#ifndef STATUS_INSTRUCTION_MISALIGNMENT
+# define STATUS_INSTRUCTION_MISALIGNMENT ((NTSTATUS) 0xC00000AAL)
+#endif
+
+#ifndef STATUS_INSTANCE_NOT_AVAILABLE
+# define STATUS_INSTANCE_NOT_AVAILABLE ((NTSTATUS) 0xC00000ABL)
+#endif
+
+#ifndef STATUS_PIPE_NOT_AVAILABLE
+# define STATUS_PIPE_NOT_AVAILABLE ((NTSTATUS) 0xC00000ACL)
+#endif
+
+#ifndef STATUS_INVALID_PIPE_STATE
+# define STATUS_INVALID_PIPE_STATE ((NTSTATUS) 0xC00000ADL)
+#endif
+
+#ifndef STATUS_PIPE_BUSY
+# define STATUS_PIPE_BUSY ((NTSTATUS) 0xC00000AEL)
+#endif
+
+#ifndef STATUS_ILLEGAL_FUNCTION
+# define STATUS_ILLEGAL_FUNCTION ((NTSTATUS) 0xC00000AFL)
+#endif
+
+#ifndef STATUS_PIPE_DISCONNECTED
+# define STATUS_PIPE_DISCONNECTED ((NTSTATUS) 0xC00000B0L)
+#endif
+
+#ifndef STATUS_PIPE_CLOSING
+# define STATUS_PIPE_CLOSING ((NTSTATUS) 0xC00000B1L)
+#endif
+
+#ifndef STATUS_PIPE_CONNECTED
+# define STATUS_PIPE_CONNECTED ((NTSTATUS) 0xC00000B2L)
+#endif
+
+#ifndef STATUS_PIPE_LISTENING
+# define STATUS_PIPE_LISTENING ((NTSTATUS) 0xC00000B3L)
+#endif
+
+#ifndef STATUS_INVALID_READ_MODE
+# define STATUS_INVALID_READ_MODE ((NTSTATUS) 0xC00000B4L)
+#endif
+
+#ifndef STATUS_IO_TIMEOUT
+# define STATUS_IO_TIMEOUT ((NTSTATUS) 0xC00000B5L)
+#endif
+
+#ifndef STATUS_FILE_FORCED_CLOSED
+# define STATUS_FILE_FORCED_CLOSED ((NTSTATUS) 0xC00000B6L)
+#endif
+
+#ifndef STATUS_PROFILING_NOT_STARTED
+# define STATUS_PROFILING_NOT_STARTED ((NTSTATUS) 0xC00000B7L)
+#endif
+
+#ifndef STATUS_PROFILING_NOT_STOPPED
+# define STATUS_PROFILING_NOT_STOPPED ((NTSTATUS) 0xC00000B8L)
+#endif
+
+#ifndef STATUS_COULD_NOT_INTERPRET
+# define STATUS_COULD_NOT_INTERPRET ((NTSTATUS) 0xC00000B9L)
+#endif
+
+#ifndef STATUS_FILE_IS_A_DIRECTORY
+# define STATUS_FILE_IS_A_DIRECTORY ((NTSTATUS) 0xC00000BAL)
+#endif
+
+#ifndef STATUS_NOT_SUPPORTED
+# define STATUS_NOT_SUPPORTED ((NTSTATUS) 0xC00000BBL)
+#endif
+
+#ifndef STATUS_REMOTE_NOT_LISTENING
+# define STATUS_REMOTE_NOT_LISTENING ((NTSTATUS) 0xC00000BCL)
+#endif
+
+#ifndef STATUS_DUPLICATE_NAME
+# define STATUS_DUPLICATE_NAME ((NTSTATUS) 0xC00000BDL)
+#endif
+
+#ifndef STATUS_BAD_NETWORK_PATH
+# define STATUS_BAD_NETWORK_PATH ((NTSTATUS) 0xC00000BEL)
+#endif
+
+#ifndef STATUS_NETWORK_BUSY
+# define STATUS_NETWORK_BUSY ((NTSTATUS) 0xC00000BFL)
+#endif
+
+#ifndef STATUS_DEVICE_DOES_NOT_EXIST
+# define STATUS_DEVICE_DOES_NOT_EXIST ((NTSTATUS) 0xC00000C0L)
+#endif
+
+#ifndef STATUS_TOO_MANY_COMMANDS
+# define STATUS_TOO_MANY_COMMANDS ((NTSTATUS) 0xC00000C1L)
+#endif
+
+#ifndef STATUS_ADAPTER_HARDWARE_ERROR
+# define STATUS_ADAPTER_HARDWARE_ERROR ((NTSTATUS) 0xC00000C2L)
+#endif
+
+#ifndef STATUS_INVALID_NETWORK_RESPONSE
+# define STATUS_INVALID_NETWORK_RESPONSE ((NTSTATUS) 0xC00000C3L)
+#endif
+
+#ifndef STATUS_UNEXPECTED_NETWORK_ERROR
+# define STATUS_UNEXPECTED_NETWORK_ERROR ((NTSTATUS) 0xC00000C4L)
+#endif
+
+#ifndef STATUS_BAD_REMOTE_ADAPTER
+# define STATUS_BAD_REMOTE_ADAPTER ((NTSTATUS) 0xC00000C5L)
+#endif
+
+#ifndef STATUS_PRINT_QUEUE_FULL
+# define STATUS_PRINT_QUEUE_FULL ((NTSTATUS) 0xC00000C6L)
+#endif
+
+#ifndef STATUS_NO_SPOOL_SPACE
+# define STATUS_NO_SPOOL_SPACE ((NTSTATUS) 0xC00000C7L)
+#endif
+
+#ifndef STATUS_PRINT_CANCELLED
+# define STATUS_PRINT_CANCELLED ((NTSTATUS) 0xC00000C8L)
+#endif
+
+#ifndef STATUS_NETWORK_NAME_DELETED
+# define STATUS_NETWORK_NAME_DELETED ((NTSTATUS) 0xC00000C9L)
+#endif
+
+#ifndef STATUS_NETWORK_ACCESS_DENIED
+# define STATUS_NETWORK_ACCESS_DENIED ((NTSTATUS) 0xC00000CAL)
+#endif
+
+#ifndef STATUS_BAD_DEVICE_TYPE
+# define STATUS_BAD_DEVICE_TYPE ((NTSTATUS) 0xC00000CBL)
+#endif
+
+#ifndef STATUS_BAD_NETWORK_NAME
+# define STATUS_BAD_NETWORK_NAME ((NTSTATUS) 0xC00000CCL)
+#endif
+
+#ifndef STATUS_TOO_MANY_NAMES
+# define STATUS_TOO_MANY_NAMES ((NTSTATUS) 0xC00000CDL)
+#endif
+
+#ifndef STATUS_TOO_MANY_SESSIONS
+# define STATUS_TOO_MANY_SESSIONS ((NTSTATUS) 0xC00000CEL)
+#endif
+
+#ifndef STATUS_SHARING_PAUSED
+# define STATUS_SHARING_PAUSED ((NTSTATUS) 0xC00000CFL)
+#endif
+
+#ifndef STATUS_REQUEST_NOT_ACCEPTED
+# define STATUS_REQUEST_NOT_ACCEPTED ((NTSTATUS) 0xC00000D0L)
+#endif
+
+#ifndef STATUS_REDIRECTOR_PAUSED
+# define STATUS_REDIRECTOR_PAUSED ((NTSTATUS) 0xC00000D1L)
+#endif
+
+#ifndef STATUS_NET_WRITE_FAULT
+# define STATUS_NET_WRITE_FAULT ((NTSTATUS) 0xC00000D2L)
+#endif
+
+#ifndef STATUS_PROFILING_AT_LIMIT
+# define STATUS_PROFILING_AT_LIMIT ((NTSTATUS) 0xC00000D3L)
+#endif
+
+#ifndef STATUS_NOT_SAME_DEVICE
+# define STATUS_NOT_SAME_DEVICE ((NTSTATUS) 0xC00000D4L)
+#endif
+
+#ifndef STATUS_FILE_RENAMED
+# define STATUS_FILE_RENAMED ((NTSTATUS) 0xC00000D5L)
+#endif
+
+#ifndef STATUS_VIRTUAL_CIRCUIT_CLOSED
+# define STATUS_VIRTUAL_CIRCUIT_CLOSED ((NTSTATUS) 0xC00000D6L)
+#endif
+
+#ifndef STATUS_NO_SECURITY_ON_OBJECT
+# define STATUS_NO_SECURITY_ON_OBJECT ((NTSTATUS) 0xC00000D7L)
+#endif
+
+#ifndef STATUS_CANT_WAIT
+# define STATUS_CANT_WAIT ((NTSTATUS) 0xC00000D8L)
+#endif
+
+#ifndef STATUS_PIPE_EMPTY
+# define STATUS_PIPE_EMPTY ((NTSTATUS) 0xC00000D9L)
+#endif
+
+#ifndef STATUS_CANT_ACCESS_DOMAIN_INFO
+# define STATUS_CANT_ACCESS_DOMAIN_INFO ((NTSTATUS) 0xC00000DAL)
+#endif
+
+#ifndef STATUS_CANT_TERMINATE_SELF
+# define STATUS_CANT_TERMINATE_SELF ((NTSTATUS) 0xC00000DBL)
+#endif
+
+#ifndef STATUS_INVALID_SERVER_STATE
+# define STATUS_INVALID_SERVER_STATE ((NTSTATUS) 0xC00000DCL)
+#endif
+
+#ifndef STATUS_INVALID_DOMAIN_STATE
+# define STATUS_INVALID_DOMAIN_STATE ((NTSTATUS) 0xC00000DDL)
+#endif
+
+#ifndef STATUS_INVALID_DOMAIN_ROLE
+# define STATUS_INVALID_DOMAIN_ROLE ((NTSTATUS) 0xC00000DEL)
+#endif
+
+#ifndef STATUS_NO_SUCH_DOMAIN
+# define STATUS_NO_SUCH_DOMAIN ((NTSTATUS) 0xC00000DFL)
+#endif
+
+#ifndef STATUS_DOMAIN_EXISTS
+# define STATUS_DOMAIN_EXISTS ((NTSTATUS) 0xC00000E0L)
+#endif
+
+#ifndef STATUS_DOMAIN_LIMIT_EXCEEDED
+# define STATUS_DOMAIN_LIMIT_EXCEEDED ((NTSTATUS) 0xC00000E1L)
+#endif
+
+#ifndef STATUS_OPLOCK_NOT_GRANTED
+# define STATUS_OPLOCK_NOT_GRANTED ((NTSTATUS) 0xC00000E2L)
+#endif
+
+#ifndef STATUS_INVALID_OPLOCK_PROTOCOL
+# define STATUS_INVALID_OPLOCK_PROTOCOL ((NTSTATUS) 0xC00000E3L)
+#endif
+
+#ifndef STATUS_INTERNAL_DB_CORRUPTION
+# define STATUS_INTERNAL_DB_CORRUPTION ((NTSTATUS) 0xC00000E4L)
+#endif
+
+#ifndef STATUS_INTERNAL_ERROR
+# define STATUS_INTERNAL_ERROR ((NTSTATUS) 0xC00000E5L)
+#endif
+
+#ifndef STATUS_GENERIC_NOT_MAPPED
+# define STATUS_GENERIC_NOT_MAPPED ((NTSTATUS) 0xC00000E6L)
+#endif
+
+#ifndef STATUS_BAD_DESCRIPTOR_FORMAT
+# define STATUS_BAD_DESCRIPTOR_FORMAT ((NTSTATUS) 0xC00000E7L)
+#endif
+
+#ifndef STATUS_INVALID_USER_BUFFER
+# define STATUS_INVALID_USER_BUFFER ((NTSTATUS) 0xC00000E8L)
+#endif
+
+#ifndef STATUS_UNEXPECTED_IO_ERROR
+# define STATUS_UNEXPECTED_IO_ERROR ((NTSTATUS) 0xC00000E9L)
+#endif
+
+#ifndef STATUS_UNEXPECTED_MM_CREATE_ERR
+# define STATUS_UNEXPECTED_MM_CREATE_ERR ((NTSTATUS) 0xC00000EAL)
+#endif
+
+#ifndef STATUS_UNEXPECTED_MM_MAP_ERROR
+# define STATUS_UNEXPECTED_MM_MAP_ERROR ((NTSTATUS) 0xC00000EBL)
+#endif
+
+#ifndef STATUS_UNEXPECTED_MM_EXTEND_ERR
+# define STATUS_UNEXPECTED_MM_EXTEND_ERR ((NTSTATUS) 0xC00000ECL)
+#endif
+
+#ifndef STATUS_NOT_LOGON_PROCESS
+# define STATUS_NOT_LOGON_PROCESS ((NTSTATUS) 0xC00000EDL)
+#endif
+
+#ifndef STATUS_LOGON_SESSION_EXISTS
+# define STATUS_LOGON_SESSION_EXISTS ((NTSTATUS) 0xC00000EEL)
+#endif
+
+#ifndef STATUS_INVALID_PARAMETER_1
+# define STATUS_INVALID_PARAMETER_1 ((NTSTATUS) 0xC00000EFL)
+#endif
+
+#ifndef STATUS_INVALID_PARAMETER_2
+# define STATUS_INVALID_PARAMETER_2 ((NTSTATUS) 0xC00000F0L)
+#endif
+
+#ifndef STATUS_INVALID_PARAMETER_3
+# define STATUS_INVALID_PARAMETER_3 ((NTSTATUS) 0xC00000F1L)
+#endif
+
+#ifndef STATUS_INVALID_PARAMETER_4
+# define STATUS_INVALID_PARAMETER_4 ((NTSTATUS) 0xC00000F2L)
+#endif
+
+#ifndef STATUS_INVALID_PARAMETER_5
+# define STATUS_INVALID_PARAMETER_5 ((NTSTATUS) 0xC00000F3L)
+#endif
+
+#ifndef STATUS_INVALID_PARAMETER_6
+# define STATUS_INVALID_PARAMETER_6 ((NTSTATUS) 0xC00000F4L)
+#endif
+
+#ifndef STATUS_INVALID_PARAMETER_7
+# define STATUS_INVALID_PARAMETER_7 ((NTSTATUS) 0xC00000F5L)
+#endif
+
+#ifndef STATUS_INVALID_PARAMETER_8
+# define STATUS_INVALID_PARAMETER_8 ((NTSTATUS) 0xC00000F6L)
+#endif
+
+#ifndef STATUS_INVALID_PARAMETER_9
+# define STATUS_INVALID_PARAMETER_9 ((NTSTATUS) 0xC00000F7L)
+#endif
+
+#ifndef STATUS_INVALID_PARAMETER_10
+# define STATUS_INVALID_PARAMETER_10 ((NTSTATUS) 0xC00000F8L)
+#endif
+
+#ifndef STATUS_INVALID_PARAMETER_11
+# define STATUS_INVALID_PARAMETER_11 ((NTSTATUS) 0xC00000F9L)
+#endif
+
+#ifndef STATUS_INVALID_PARAMETER_12
+# define STATUS_INVALID_PARAMETER_12 ((NTSTATUS) 0xC00000FAL)
+#endif
+
+#ifndef STATUS_REDIRECTOR_NOT_STARTED
+# define STATUS_REDIRECTOR_NOT_STARTED ((NTSTATUS) 0xC00000FBL)
+#endif
+
+#ifndef STATUS_REDIRECTOR_STARTED
+# define STATUS_REDIRECTOR_STARTED ((NTSTATUS) 0xC00000FCL)
+#endif
+
+#ifndef STATUS_STACK_OVERFLOW
+# define STATUS_STACK_OVERFLOW ((NTSTATUS) 0xC00000FDL)
+#endif
+
+#ifndef STATUS_NO_SUCH_PACKAGE
+# define STATUS_NO_SUCH_PACKAGE ((NTSTATUS) 0xC00000FEL)
+#endif
+
+#ifndef STATUS_BAD_FUNCTION_TABLE
+# define STATUS_BAD_FUNCTION_TABLE ((NTSTATUS) 0xC00000FFL)
+#endif
+
+#ifndef STATUS_VARIABLE_NOT_FOUND
+# define STATUS_VARIABLE_NOT_FOUND ((NTSTATUS) 0xC0000100L)
+#endif
+
+#ifndef STATUS_DIRECTORY_NOT_EMPTY
+# define STATUS_DIRECTORY_NOT_EMPTY ((NTSTATUS) 0xC0000101L)
+#endif
+
+#ifndef STATUS_FILE_CORRUPT_ERROR
+# define STATUS_FILE_CORRUPT_ERROR ((NTSTATUS) 0xC0000102L)
+#endif
+
+#ifndef STATUS_NOT_A_DIRECTORY
+# define STATUS_NOT_A_DIRECTORY ((NTSTATUS) 0xC0000103L)
+#endif
+
+#ifndef STATUS_BAD_LOGON_SESSION_STATE
+# define STATUS_BAD_LOGON_SESSION_STATE ((NTSTATUS) 0xC0000104L)
+#endif
+
+#ifndef STATUS_LOGON_SESSION_COLLISION
+# define STATUS_LOGON_SESSION_COLLISION ((NTSTATUS) 0xC0000105L)
+#endif
+
+#ifndef STATUS_NAME_TOO_LONG
+# define STATUS_NAME_TOO_LONG ((NTSTATUS) 0xC0000106L)
+#endif
+
+#ifndef STATUS_FILES_OPEN
+# define STATUS_FILES_OPEN ((NTSTATUS) 0xC0000107L)
+#endif
+
+#ifndef STATUS_CONNECTION_IN_USE
+# define STATUS_CONNECTION_IN_USE ((NTSTATUS) 0xC0000108L)
+#endif
+
+#ifndef STATUS_MESSAGE_NOT_FOUND
+# define STATUS_MESSAGE_NOT_FOUND ((NTSTATUS) 0xC0000109L)
+#endif
+
+#ifndef STATUS_PROCESS_IS_TERMINATING
+# define STATUS_PROCESS_IS_TERMINATING ((NTSTATUS) 0xC000010AL)
+#endif
+
+#ifndef STATUS_INVALID_LOGON_TYPE
+# define STATUS_INVALID_LOGON_TYPE ((NTSTATUS) 0xC000010BL)
+#endif
+
+#ifndef STATUS_NO_GUID_TRANSLATION
+# define STATUS_NO_GUID_TRANSLATION ((NTSTATUS) 0xC000010CL)
+#endif
+
+#ifndef STATUS_CANNOT_IMPERSONATE
+# define STATUS_CANNOT_IMPERSONATE ((NTSTATUS) 0xC000010DL)
+#endif
+
+#ifndef STATUS_IMAGE_ALREADY_LOADED
+# define STATUS_IMAGE_ALREADY_LOADED ((NTSTATUS) 0xC000010EL)
+#endif
+
+#ifndef STATUS_ABIOS_NOT_PRESENT
+# define STATUS_ABIOS_NOT_PRESENT ((NTSTATUS) 0xC000010FL)
+#endif
+
+#ifndef STATUS_ABIOS_LID_NOT_EXIST
+# define STATUS_ABIOS_LID_NOT_EXIST ((NTSTATUS) 0xC0000110L)
+#endif
+
+#ifndef STATUS_ABIOS_LID_ALREADY_OWNED
+# define STATUS_ABIOS_LID_ALREADY_OWNED ((NTSTATUS) 0xC0000111L)
+#endif
+
+#ifndef STATUS_ABIOS_NOT_LID_OWNER
+# define STATUS_ABIOS_NOT_LID_OWNER ((NTSTATUS) 0xC0000112L)
+#endif
+
+#ifndef STATUS_ABIOS_INVALID_COMMAND
+# define STATUS_ABIOS_INVALID_COMMAND ((NTSTATUS) 0xC0000113L)
+#endif
+
+#ifndef STATUS_ABIOS_INVALID_LID
+# define STATUS_ABIOS_INVALID_LID ((NTSTATUS) 0xC0000114L)
+#endif
+
+#ifndef STATUS_ABIOS_SELECTOR_NOT_AVAILABLE
+# define STATUS_ABIOS_SELECTOR_NOT_AVAILABLE ((NTSTATUS) 0xC0000115L)
+#endif
+
+#ifndef STATUS_ABIOS_INVALID_SELECTOR
+# define STATUS_ABIOS_INVALID_SELECTOR ((NTSTATUS) 0xC0000116L)
+#endif
+
+#ifndef STATUS_NO_LDT
+# define STATUS_NO_LDT ((NTSTATUS) 0xC0000117L)
+#endif
+
+#ifndef STATUS_INVALID_LDT_SIZE
+# define STATUS_INVALID_LDT_SIZE ((NTSTATUS) 0xC0000118L)
+#endif
+
+#ifndef STATUS_INVALID_LDT_OFFSET
+# define STATUS_INVALID_LDT_OFFSET ((NTSTATUS) 0xC0000119L)
+#endif
+
+#ifndef STATUS_INVALID_LDT_DESCRIPTOR
+# define STATUS_INVALID_LDT_DESCRIPTOR ((NTSTATUS) 0xC000011AL)
+#endif
+
+#ifndef STATUS_INVALID_IMAGE_NE_FORMAT
+# define STATUS_INVALID_IMAGE_NE_FORMAT ((NTSTATUS) 0xC000011BL)
+#endif
+
+#ifndef STATUS_RXACT_INVALID_STATE
+# define STATUS_RXACT_INVALID_STATE ((NTSTATUS) 0xC000011CL)
+#endif
+
+#ifndef STATUS_RXACT_COMMIT_FAILURE
+# define STATUS_RXACT_COMMIT_FAILURE ((NTSTATUS) 0xC000011DL)
+#endif
+
+#ifndef STATUS_MAPPED_FILE_SIZE_ZERO
+# define STATUS_MAPPED_FILE_SIZE_ZERO ((NTSTATUS) 0xC000011EL)
+#endif
+
+#ifndef STATUS_TOO_MANY_OPENED_FILES
+# define STATUS_TOO_MANY_OPENED_FILES ((NTSTATUS) 0xC000011FL)
+#endif
+
+#ifndef STATUS_CANCELLED
+# define STATUS_CANCELLED ((NTSTATUS) 0xC0000120L)
+#endif
+
+#ifndef STATUS_CANNOT_DELETE
+# define STATUS_CANNOT_DELETE ((NTSTATUS) 0xC0000121L)
+#endif
+
+#ifndef STATUS_INVALID_COMPUTER_NAME
+# define STATUS_INVALID_COMPUTER_NAME ((NTSTATUS) 0xC0000122L)
+#endif
+
+#ifndef STATUS_FILE_DELETED
+# define STATUS_FILE_DELETED ((NTSTATUS) 0xC0000123L)
+#endif
+
+#ifndef STATUS_SPECIAL_ACCOUNT
+# define STATUS_SPECIAL_ACCOUNT ((NTSTATUS) 0xC0000124L)
+#endif
+
+#ifndef STATUS_SPECIAL_GROUP
+# define STATUS_SPECIAL_GROUP ((NTSTATUS) 0xC0000125L)
+#endif
+
+#ifndef STATUS_SPECIAL_USER
+# define STATUS_SPECIAL_USER ((NTSTATUS) 0xC0000126L)
+#endif
+
+#ifndef STATUS_MEMBERS_PRIMARY_GROUP
+# define STATUS_MEMBERS_PRIMARY_GROUP ((NTSTATUS) 0xC0000127L)
+#endif
+
+#ifndef STATUS_FILE_CLOSED
+# define STATUS_FILE_CLOSED ((NTSTATUS) 0xC0000128L)
+#endif
+
+#ifndef STATUS_TOO_MANY_THREADS
+# define STATUS_TOO_MANY_THREADS ((NTSTATUS) 0xC0000129L)
+#endif
+
+#ifndef STATUS_THREAD_NOT_IN_PROCESS
+# define STATUS_THREAD_NOT_IN_PROCESS ((NTSTATUS) 0xC000012AL)
+#endif
+
+#ifndef STATUS_TOKEN_ALREADY_IN_USE
+# define STATUS_TOKEN_ALREADY_IN_USE ((NTSTATUS) 0xC000012BL)
+#endif
+
+#ifndef STATUS_PAGEFILE_QUOTA_EXCEEDED
+# define STATUS_PAGEFILE_QUOTA_EXCEEDED ((NTSTATUS) 0xC000012CL)
+#endif
+
+#ifndef STATUS_COMMITMENT_LIMIT
+# define STATUS_COMMITMENT_LIMIT ((NTSTATUS) 0xC000012DL)
+#endif
+
+#ifndef STATUS_INVALID_IMAGE_LE_FORMAT
+# define STATUS_INVALID_IMAGE_LE_FORMAT ((NTSTATUS) 0xC000012EL)
+#endif
+
+#ifndef STATUS_INVALID_IMAGE_NOT_MZ
+# define STATUS_INVALID_IMAGE_NOT_MZ ((NTSTATUS) 0xC000012FL)
+#endif
+
+#ifndef STATUS_INVALID_IMAGE_PROTECT
+# define STATUS_INVALID_IMAGE_PROTECT ((NTSTATUS) 0xC0000130L)
+#endif
+
+#ifndef STATUS_INVALID_IMAGE_WIN_16
+# define STATUS_INVALID_IMAGE_WIN_16 ((NTSTATUS) 0xC0000131L)
+#endif
+
+#ifndef STATUS_LOGON_SERVER_CONFLICT
+# define STATUS_LOGON_SERVER_CONFLICT ((NTSTATUS) 0xC0000132L)
+#endif
+
+#ifndef STATUS_TIME_DIFFERENCE_AT_DC
+# define STATUS_TIME_DIFFERENCE_AT_DC ((NTSTATUS) 0xC0000133L)
+#endif
+
+#ifndef STATUS_SYNCHRONIZATION_REQUIRED
+# define STATUS_SYNCHRONIZATION_REQUIRED ((NTSTATUS) 0xC0000134L)
+#endif
+
+#ifndef STATUS_DLL_NOT_FOUND
+# define STATUS_DLL_NOT_FOUND ((NTSTATUS) 0xC0000135L)
+#endif
+
+#ifndef STATUS_OPEN_FAILED
+# define STATUS_OPEN_FAILED ((NTSTATUS) 0xC0000136L)
+#endif
+
+#ifndef STATUS_IO_PRIVILEGE_FAILED
+# define STATUS_IO_PRIVILEGE_FAILED ((NTSTATUS) 0xC0000137L)
+#endif
+
+#ifndef STATUS_ORDINAL_NOT_FOUND
+# define STATUS_ORDINAL_NOT_FOUND ((NTSTATUS) 0xC0000138L)
+#endif
+
+#ifndef STATUS_ENTRYPOINT_NOT_FOUND
+# define STATUS_ENTRYPOINT_NOT_FOUND ((NTSTATUS) 0xC0000139L)
+#endif
+
+#ifndef STATUS_CONTROL_C_EXIT
+# define STATUS_CONTROL_C_EXIT ((NTSTATUS) 0xC000013AL)
+#endif
+
+#ifndef STATUS_LOCAL_DISCONNECT
+# define STATUS_LOCAL_DISCONNECT ((NTSTATUS) 0xC000013BL)
+#endif
+
+#ifndef STATUS_REMOTE_DISCONNECT
+# define STATUS_REMOTE_DISCONNECT ((NTSTATUS) 0xC000013CL)
+#endif
+
+#ifndef STATUS_REMOTE_RESOURCES
+# define STATUS_REMOTE_RESOURCES ((NTSTATUS) 0xC000013DL)
+#endif
+
+#ifndef STATUS_LINK_FAILED
+# define STATUS_LINK_FAILED ((NTSTATUS) 0xC000013EL)
+#endif
+
+#ifndef STATUS_LINK_TIMEOUT
+# define STATUS_LINK_TIMEOUT ((NTSTATUS) 0xC000013FL)
+#endif
+
+#ifndef STATUS_INVALID_CONNECTION
+# define STATUS_INVALID_CONNECTION ((NTSTATUS) 0xC0000140L)
+#endif
+
+#ifndef STATUS_INVALID_ADDRESS
+# define STATUS_INVALID_ADDRESS ((NTSTATUS) 0xC0000141L)
+#endif
+
+#ifndef STATUS_DLL_INIT_FAILED
+# define STATUS_DLL_INIT_FAILED ((NTSTATUS) 0xC0000142L)
+#endif
+
+#ifndef STATUS_MISSING_SYSTEMFILE
+# define STATUS_MISSING_SYSTEMFILE ((NTSTATUS) 0xC0000143L)
+#endif
+
+#ifndef STATUS_UNHANDLED_EXCEPTION
+# define STATUS_UNHANDLED_EXCEPTION ((NTSTATUS) 0xC0000144L)
+#endif
+
+#ifndef STATUS_APP_INIT_FAILURE
+# define STATUS_APP_INIT_FAILURE ((NTSTATUS) 0xC0000145L)
+#endif
+
+#ifndef STATUS_PAGEFILE_CREATE_FAILED
+# define STATUS_PAGEFILE_CREATE_FAILED ((NTSTATUS) 0xC0000146L)
+#endif
+
+#ifndef STATUS_NO_PAGEFILE
+# define STATUS_NO_PAGEFILE ((NTSTATUS) 0xC0000147L)
+#endif
+
+#ifndef STATUS_INVALID_LEVEL
+# define STATUS_INVALID_LEVEL ((NTSTATUS) 0xC0000148L)
+#endif
+
+#ifndef STATUS_WRONG_PASSWORD_CORE
+# define STATUS_WRONG_PASSWORD_CORE ((NTSTATUS) 0xC0000149L)
+#endif
+
+#ifndef STATUS_ILLEGAL_FLOAT_CONTEXT
+# define STATUS_ILLEGAL_FLOAT_CONTEXT ((NTSTATUS) 0xC000014AL)
+#endif
+
+#ifndef STATUS_PIPE_BROKEN
+# define STATUS_PIPE_BROKEN ((NTSTATUS) 0xC000014BL)
+#endif
+
+#ifndef STATUS_REGISTRY_CORRUPT
+# define STATUS_REGISTRY_CORRUPT ((NTSTATUS) 0xC000014CL)
+#endif
+
+#ifndef STATUS_REGISTRY_IO_FAILED
+# define STATUS_REGISTRY_IO_FAILED ((NTSTATUS) 0xC000014DL)
+#endif
+
+#ifndef STATUS_NO_EVENT_PAIR
+# define STATUS_NO_EVENT_PAIR ((NTSTATUS) 0xC000014EL)
+#endif
+
+#ifndef STATUS_UNRECOGNIZED_VOLUME
+# define STATUS_UNRECOGNIZED_VOLUME ((NTSTATUS) 0xC000014FL)
+#endif
+
+#ifndef STATUS_SERIAL_NO_DEVICE_INITED
+# define STATUS_SERIAL_NO_DEVICE_INITED ((NTSTATUS) 0xC0000150L)
+#endif
+
+#ifndef STATUS_NO_SUCH_ALIAS
+# define STATUS_NO_SUCH_ALIAS ((NTSTATUS) 0xC0000151L)
+#endif
+
+#ifndef STATUS_MEMBER_NOT_IN_ALIAS
+# define STATUS_MEMBER_NOT_IN_ALIAS ((NTSTATUS) 0xC0000152L)
+#endif
+
+#ifndef STATUS_MEMBER_IN_ALIAS
+# define STATUS_MEMBER_IN_ALIAS ((NTSTATUS) 0xC0000153L)
+#endif
+
+#ifndef STATUS_ALIAS_EXISTS
+# define STATUS_ALIAS_EXISTS ((NTSTATUS) 0xC0000154L)
+#endif
+
+#ifndef STATUS_LOGON_NOT_GRANTED
+# define STATUS_LOGON_NOT_GRANTED ((NTSTATUS) 0xC0000155L)
+#endif
+
+#ifndef STATUS_TOO_MANY_SECRETS
+# define STATUS_TOO_MANY_SECRETS ((NTSTATUS) 0xC0000156L)
+#endif
+
+#ifndef STATUS_SECRET_TOO_LONG
+# define STATUS_SECRET_TOO_LONG ((NTSTATUS) 0xC0000157L)
+#endif
+
+#ifndef STATUS_INTERNAL_DB_ERROR
+# define STATUS_INTERNAL_DB_ERROR ((NTSTATUS) 0xC0000158L)
+#endif
+
+#ifndef STATUS_FULLSCREEN_MODE
+# define STATUS_FULLSCREEN_MODE ((NTSTATUS) 0xC0000159L)
+#endif
+
+#ifndef STATUS_TOO_MANY_CONTEXT_IDS
+# define STATUS_TOO_MANY_CONTEXT_IDS ((NTSTATUS) 0xC000015AL)
+#endif
+
+#ifndef STATUS_LOGON_TYPE_NOT_GRANTED
+# define STATUS_LOGON_TYPE_NOT_GRANTED ((NTSTATUS) 0xC000015BL)
+#endif
+
+#ifndef STATUS_NOT_REGISTRY_FILE
+# define STATUS_NOT_REGISTRY_FILE ((NTSTATUS) 0xC000015CL)
+#endif
+
+#ifndef STATUS_NT_CROSS_ENCRYPTION_REQUIRED
+# define STATUS_NT_CROSS_ENCRYPTION_REQUIRED ((NTSTATUS) 0xC000015DL)
+#endif
+
+#ifndef STATUS_DOMAIN_CTRLR_CONFIG_ERROR
+# define STATUS_DOMAIN_CTRLR_CONFIG_ERROR ((NTSTATUS) 0xC000015EL)
+#endif
+
+#ifndef STATUS_FT_MISSING_MEMBER
+# define STATUS_FT_MISSING_MEMBER ((NTSTATUS) 0xC000015FL)
+#endif
+
+#ifndef STATUS_ILL_FORMED_SERVICE_ENTRY
+# define STATUS_ILL_FORMED_SERVICE_ENTRY ((NTSTATUS) 0xC0000160L)
+#endif
+
+#ifndef STATUS_ILLEGAL_CHARACTER
+# define STATUS_ILLEGAL_CHARACTER ((NTSTATUS) 0xC0000161L)
+#endif
+
+#ifndef STATUS_UNMAPPABLE_CHARACTER
+# define STATUS_UNMAPPABLE_CHARACTER ((NTSTATUS) 0xC0000162L)
+#endif
+
+#ifndef STATUS_UNDEFINED_CHARACTER
+# define STATUS_UNDEFINED_CHARACTER ((NTSTATUS) 0xC0000163L)
+#endif
+
+#ifndef STATUS_FLOPPY_VOLUME
+# define STATUS_FLOPPY_VOLUME ((NTSTATUS) 0xC0000164L)
+#endif
+
+#ifndef STATUS_FLOPPY_ID_MARK_NOT_FOUND
+# define STATUS_FLOPPY_ID_MARK_NOT_FOUND ((NTSTATUS) 0xC0000165L)
+#endif
+
+#ifndef STATUS_FLOPPY_WRONG_CYLINDER
+# define STATUS_FLOPPY_WRONG_CYLINDER ((NTSTATUS) 0xC0000166L)
+#endif
+
+#ifndef STATUS_FLOPPY_UNKNOWN_ERROR
+# define STATUS_FLOPPY_UNKNOWN_ERROR ((NTSTATUS) 0xC0000167L)
+#endif
+
+#ifndef STATUS_FLOPPY_BAD_REGISTERS
+# define STATUS_FLOPPY_BAD_REGISTERS ((NTSTATUS) 0xC0000168L)
+#endif
+
+#ifndef STATUS_DISK_RECALIBRATE_FAILED
+# define STATUS_DISK_RECALIBRATE_FAILED ((NTSTATUS) 0xC0000169L)
+#endif
+
+#ifndef STATUS_DISK_OPERATION_FAILED
+# define STATUS_DISK_OPERATION_FAILED ((NTSTATUS) 0xC000016AL)
+#endif
+
+#ifndef STATUS_DISK_RESET_FAILED
+# define STATUS_DISK_RESET_FAILED ((NTSTATUS) 0xC000016BL)
+#endif
+
+#ifndef STATUS_SHARED_IRQ_BUSY
+# define STATUS_SHARED_IRQ_BUSY ((NTSTATUS) 0xC000016CL)
+#endif
+
+#ifndef STATUS_FT_ORPHANING
+# define STATUS_FT_ORPHANING ((NTSTATUS) 0xC000016DL)
+#endif
+
+#ifndef STATUS_BIOS_FAILED_TO_CONNECT_INTERRUPT
+# define STATUS_BIOS_FAILED_TO_CONNECT_INTERRUPT ((NTSTATUS) 0xC000016EL)
+#endif
+
+#ifndef STATUS_PARTITION_FAILURE
+# define STATUS_PARTITION_FAILURE ((NTSTATUS) 0xC0000172L)
+#endif
+
+#ifndef STATUS_INVALID_BLOCK_LENGTH
+# define STATUS_INVALID_BLOCK_LENGTH ((NTSTATUS) 0xC0000173L)
+#endif
+
+#ifndef STATUS_DEVICE_NOT_PARTITIONED
+# define STATUS_DEVICE_NOT_PARTITIONED ((NTSTATUS) 0xC0000174L)
+#endif
+
+#ifndef STATUS_UNABLE_TO_LOCK_MEDIA
+# define STATUS_UNABLE_TO_LOCK_MEDIA ((NTSTATUS) 0xC0000175L)
+#endif
+
+#ifndef STATUS_UNABLE_TO_UNLOAD_MEDIA
+# define STATUS_UNABLE_TO_UNLOAD_MEDIA ((NTSTATUS) 0xC0000176L)
+#endif
+
+#ifndef STATUS_EOM_OVERFLOW
+# define STATUS_EOM_OVERFLOW ((NTSTATUS) 0xC0000177L)
+#endif
+
+#ifndef STATUS_NO_MEDIA
+# define STATUS_NO_MEDIA ((NTSTATUS) 0xC0000178L)
+#endif
+
+#ifndef STATUS_NO_SUCH_MEMBER
+# define STATUS_NO_SUCH_MEMBER ((NTSTATUS) 0xC000017AL)
+#endif
+
+#ifndef STATUS_INVALID_MEMBER
+# define STATUS_INVALID_MEMBER ((NTSTATUS) 0xC000017BL)
+#endif
+
+#ifndef STATUS_KEY_DELETED
+# define STATUS_KEY_DELETED ((NTSTATUS) 0xC000017CL)
+#endif
+
+#ifndef STATUS_NO_LOG_SPACE
+# define STATUS_NO_LOG_SPACE ((NTSTATUS) 0xC000017DL)
+#endif
+
+#ifndef STATUS_TOO_MANY_SIDS
+# define STATUS_TOO_MANY_SIDS ((NTSTATUS) 0xC000017EL)
+#endif
+
+#ifndef STATUS_LM_CROSS_ENCRYPTION_REQUIRED
+# define STATUS_LM_CROSS_ENCRYPTION_REQUIRED ((NTSTATUS) 0xC000017FL)
+#endif
+
+#ifndef STATUS_KEY_HAS_CHILDREN
+# define STATUS_KEY_HAS_CHILDREN ((NTSTATUS) 0xC0000180L)
+#endif
+
+#ifndef STATUS_CHILD_MUST_BE_VOLATILE
+# define STATUS_CHILD_MUST_BE_VOLATILE ((NTSTATUS) 0xC0000181L)
+#endif
+
+#ifndef STATUS_DEVICE_CONFIGURATION_ERROR
+# define STATUS_DEVICE_CONFIGURATION_ERROR ((NTSTATUS) 0xC0000182L)
+#endif
+
+#ifndef STATUS_DRIVER_INTERNAL_ERROR
+# define STATUS_DRIVER_INTERNAL_ERROR ((NTSTATUS) 0xC0000183L)
+#endif
+
+#ifndef STATUS_INVALID_DEVICE_STATE
+# define STATUS_INVALID_DEVICE_STATE ((NTSTATUS) 0xC0000184L)
+#endif
+
+#ifndef STATUS_IO_DEVICE_ERROR
+# define STATUS_IO_DEVICE_ERROR ((NTSTATUS) 0xC0000185L)
+#endif
+
+#ifndef STATUS_DEVICE_PROTOCOL_ERROR
+# define STATUS_DEVICE_PROTOCOL_ERROR ((NTSTATUS) 0xC0000186L)
+#endif
+
+#ifndef STATUS_BACKUP_CONTROLLER
+# define STATUS_BACKUP_CONTROLLER ((NTSTATUS) 0xC0000187L)
+#endif
+
+#ifndef STATUS_LOG_FILE_FULL
+# define STATUS_LOG_FILE_FULL ((NTSTATUS) 0xC0000188L)
+#endif
+
+#ifndef STATUS_TOO_LATE
+# define STATUS_TOO_LATE ((NTSTATUS) 0xC0000189L)
+#endif
+
+#ifndef STATUS_NO_TRUST_LSA_SECRET
+# define STATUS_NO_TRUST_LSA_SECRET ((NTSTATUS) 0xC000018AL)
+#endif
+
+#ifndef STATUS_NO_TRUST_SAM_ACCOUNT
+# define STATUS_NO_TRUST_SAM_ACCOUNT ((NTSTATUS) 0xC000018BL)
+#endif
+
+#ifndef STATUS_TRUSTED_DOMAIN_FAILURE
+# define STATUS_TRUSTED_DOMAIN_FAILURE ((NTSTATUS) 0xC000018CL)
+#endif
+
+#ifndef STATUS_TRUSTED_RELATIONSHIP_FAILURE
+# define STATUS_TRUSTED_RELATIONSHIP_FAILURE ((NTSTATUS) 0xC000018DL)
+#endif
+
+#ifndef STATUS_EVENTLOG_FILE_CORRUPT
+# define STATUS_EVENTLOG_FILE_CORRUPT ((NTSTATUS) 0xC000018EL)
+#endif
+
+#ifndef STATUS_EVENTLOG_CANT_START
+# define STATUS_EVENTLOG_CANT_START ((NTSTATUS) 0xC000018FL)
+#endif
+
+#ifndef STATUS_TRUST_FAILURE
+# define STATUS_TRUST_FAILURE ((NTSTATUS) 0xC0000190L)
+#endif
+
+#ifndef STATUS_MUTANT_LIMIT_EXCEEDED
+# define STATUS_MUTANT_LIMIT_EXCEEDED ((NTSTATUS) 0xC0000191L)
+#endif
+
+#ifndef STATUS_NETLOGON_NOT_STARTED
+# define STATUS_NETLOGON_NOT_STARTED ((NTSTATUS) 0xC0000192L)
+#endif
+
+#ifndef STATUS_ACCOUNT_EXPIRED
+# define STATUS_ACCOUNT_EXPIRED ((NTSTATUS) 0xC0000193L)
+#endif
+
+#ifndef STATUS_POSSIBLE_DEADLOCK
+# define STATUS_POSSIBLE_DEADLOCK ((NTSTATUS) 0xC0000194L)
+#endif
+
+#ifndef STATUS_NETWORK_CREDENTIAL_CONFLICT
+# define STATUS_NETWORK_CREDENTIAL_CONFLICT ((NTSTATUS) 0xC0000195L)
+#endif
+
+#ifndef STATUS_REMOTE_SESSION_LIMIT
+# define STATUS_REMOTE_SESSION_LIMIT ((NTSTATUS) 0xC0000196L)
+#endif
+
+#ifndef STATUS_EVENTLOG_FILE_CHANGED
+# define STATUS_EVENTLOG_FILE_CHANGED ((NTSTATUS) 0xC0000197L)
+#endif
+
+#ifndef STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT
+# define STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT ((NTSTATUS) 0xC0000198L)
+#endif
+
+#ifndef STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT
+# define STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT ((NTSTATUS) 0xC0000199L)
+#endif
+
+#ifndef STATUS_NOLOGON_SERVER_TRUST_ACCOUNT
+# define STATUS_NOLOGON_SERVER_TRUST_ACCOUNT ((NTSTATUS) 0xC000019AL)
+#endif
+
+#ifndef STATUS_DOMAIN_TRUST_INCONSISTENT
+# define STATUS_DOMAIN_TRUST_INCONSISTENT ((NTSTATUS) 0xC000019BL)
+#endif
+
+#ifndef STATUS_FS_DRIVER_REQUIRED
+# define STATUS_FS_DRIVER_REQUIRED ((NTSTATUS) 0xC000019CL)
+#endif
+
+#ifndef STATUS_IMAGE_ALREADY_LOADED_AS_DLL
+# define STATUS_IMAGE_ALREADY_LOADED_AS_DLL ((NTSTATUS) 0xC000019DL)
+#endif
+
+#ifndef STATUS_INCOMPATIBLE_WITH_GLOBAL_SHORT_NAME_REGISTRY_SETTING
+# define STATUS_INCOMPATIBLE_WITH_GLOBAL_SHORT_NAME_REGISTRY_SETTING ((NTSTATUS) 0xC000019EL)
+#endif
+
+#ifndef STATUS_SHORT_NAMES_NOT_ENABLED_ON_VOLUME
+# define STATUS_SHORT_NAMES_NOT_ENABLED_ON_VOLUME ((NTSTATUS) 0xC000019FL)
+#endif
+
+#ifndef STATUS_SECURITY_STREAM_IS_INCONSISTENT
+# define STATUS_SECURITY_STREAM_IS_INCONSISTENT ((NTSTATUS) 0xC00001A0L)
+#endif
+
+#ifndef STATUS_INVALID_LOCK_RANGE
+# define STATUS_INVALID_LOCK_RANGE ((NTSTATUS) 0xC00001A1L)
+#endif
+
+#ifndef STATUS_INVALID_ACE_CONDITION
+# define STATUS_INVALID_ACE_CONDITION ((NTSTATUS) 0xC00001A2L)
+#endif
+
+#ifndef STATUS_IMAGE_SUBSYSTEM_NOT_PRESENT
+# define STATUS_IMAGE_SUBSYSTEM_NOT_PRESENT ((NTSTATUS) 0xC00001A3L)
+#endif
+
+#ifndef STATUS_NOTIFICATION_GUID_ALREADY_DEFINED
+# define STATUS_NOTIFICATION_GUID_ALREADY_DEFINED ((NTSTATUS) 0xC00001A4L)
+#endif
+
+#ifndef STATUS_NETWORK_OPEN_RESTRICTION
+# define STATUS_NETWORK_OPEN_RESTRICTION ((NTSTATUS) 0xC0000201L)
+#endif
+
+#ifndef STATUS_NO_USER_SESSION_KEY
+# define STATUS_NO_USER_SESSION_KEY ((NTSTATUS) 0xC0000202L)
+#endif
+
+#ifndef STATUS_USER_SESSION_DELETED
+# define STATUS_USER_SESSION_DELETED ((NTSTATUS) 0xC0000203L)
+#endif
+
+#ifndef STATUS_RESOURCE_LANG_NOT_FOUND
+# define STATUS_RESOURCE_LANG_NOT_FOUND ((NTSTATUS) 0xC0000204L)
+#endif
+
+#ifndef STATUS_INSUFF_SERVER_RESOURCES
+# define STATUS_INSUFF_SERVER_RESOURCES ((NTSTATUS) 0xC0000205L)
+#endif
+
+#ifndef STATUS_INVALID_BUFFER_SIZE
+# define STATUS_INVALID_BUFFER_SIZE ((NTSTATUS) 0xC0000206L)
+#endif
+
+#ifndef STATUS_INVALID_ADDRESS_COMPONENT
+# define STATUS_INVALID_ADDRESS_COMPONENT ((NTSTATUS) 0xC0000207L)
+#endif
+
+#ifndef STATUS_INVALID_ADDRESS_WILDCARD
+# define STATUS_INVALID_ADDRESS_WILDCARD ((NTSTATUS) 0xC0000208L)
+#endif
+
+#ifndef STATUS_TOO_MANY_ADDRESSES
+# define STATUS_TOO_MANY_ADDRESSES ((NTSTATUS) 0xC0000209L)
+#endif
+
+#ifndef STATUS_ADDRESS_ALREADY_EXISTS
+# define STATUS_ADDRESS_ALREADY_EXISTS ((NTSTATUS) 0xC000020AL)
+#endif
+
+#ifndef STATUS_ADDRESS_CLOSED
+# define STATUS_ADDRESS_CLOSED ((NTSTATUS) 0xC000020BL)
+#endif
+
+#ifndef STATUS_CONNECTION_DISCONNECTED
+# define STATUS_CONNECTION_DISCONNECTED ((NTSTATUS) 0xC000020CL)
+#endif
+
+#ifndef STATUS_CONNECTION_RESET
+# define STATUS_CONNECTION_RESET ((NTSTATUS) 0xC000020DL)
+#endif
+
+#ifndef STATUS_TOO_MANY_NODES
+# define STATUS_TOO_MANY_NODES ((NTSTATUS) 0xC000020EL)
+#endif
+
+#ifndef STATUS_TRANSACTION_ABORTED
+# define STATUS_TRANSACTION_ABORTED ((NTSTATUS) 0xC000020FL)
+#endif
+
+#ifndef STATUS_TRANSACTION_TIMED_OUT
+# define STATUS_TRANSACTION_TIMED_OUT ((NTSTATUS) 0xC0000210L)
+#endif
+
+#ifndef STATUS_TRANSACTION_NO_RELEASE
+# define STATUS_TRANSACTION_NO_RELEASE ((NTSTATUS) 0xC0000211L)
+#endif
+
+#ifndef STATUS_TRANSACTION_NO_MATCH
+# define STATUS_TRANSACTION_NO_MATCH ((NTSTATUS) 0xC0000212L)
+#endif
+
+#ifndef STATUS_TRANSACTION_RESPONDED
+# define STATUS_TRANSACTION_RESPONDED ((NTSTATUS) 0xC0000213L)
+#endif
+
+#ifndef STATUS_TRANSACTION_INVALID_ID
+# define STATUS_TRANSACTION_INVALID_ID ((NTSTATUS) 0xC0000214L)
+#endif
+
+#ifndef STATUS_TRANSACTION_INVALID_TYPE
+# define STATUS_TRANSACTION_INVALID_TYPE ((NTSTATUS) 0xC0000215L)
+#endif
+
+#ifndef STATUS_NOT_SERVER_SESSION
+# define STATUS_NOT_SERVER_SESSION ((NTSTATUS) 0xC0000216L)
+#endif
+
+#ifndef STATUS_NOT_CLIENT_SESSION
+# define STATUS_NOT_CLIENT_SESSION ((NTSTATUS) 0xC0000217L)
+#endif
+
+#ifndef STATUS_CANNOT_LOAD_REGISTRY_FILE
+# define STATUS_CANNOT_LOAD_REGISTRY_FILE ((NTSTATUS) 0xC0000218L)
+#endif
+
+#ifndef STATUS_DEBUG_ATTACH_FAILED
+# define STATUS_DEBUG_ATTACH_FAILED ((NTSTATUS) 0xC0000219L)
+#endif
+
+#ifndef STATUS_SYSTEM_PROCESS_TERMINATED
+# define STATUS_SYSTEM_PROCESS_TERMINATED ((NTSTATUS) 0xC000021AL)
+#endif
+
+#ifndef STATUS_DATA_NOT_ACCEPTED
+# define STATUS_DATA_NOT_ACCEPTED ((NTSTATUS) 0xC000021BL)
+#endif
+
+#ifndef STATUS_NO_BROWSER_SERVERS_FOUND
+# define STATUS_NO_BROWSER_SERVERS_FOUND ((NTSTATUS) 0xC000021CL)
+#endif
+
+#ifndef STATUS_VDM_HARD_ERROR
+# define STATUS_VDM_HARD_ERROR ((NTSTATUS) 0xC000021DL)
+#endif
+
+#ifndef STATUS_DRIVER_CANCEL_TIMEOUT
+# define STATUS_DRIVER_CANCEL_TIMEOUT ((NTSTATUS) 0xC000021EL)
+#endif
+
+#ifndef STATUS_REPLY_MESSAGE_MISMATCH
+# define STATUS_REPLY_MESSAGE_MISMATCH ((NTSTATUS) 0xC000021FL)
+#endif
+
+#ifndef STATUS_MAPPED_ALIGNMENT
+# define STATUS_MAPPED_ALIGNMENT ((NTSTATUS) 0xC0000220L)
+#endif
+
+#ifndef STATUS_IMAGE_CHECKSUM_MISMATCH
+# define STATUS_IMAGE_CHECKSUM_MISMATCH ((NTSTATUS) 0xC0000221L)
+#endif
+
+#ifndef STATUS_LOST_WRITEBEHIND_DATA
+# define STATUS_LOST_WRITEBEHIND_DATA ((NTSTATUS) 0xC0000222L)
+#endif
+
+#ifndef STATUS_CLIENT_SERVER_PARAMETERS_INVALID
+# define STATUS_CLIENT_SERVER_PARAMETERS_INVALID ((NTSTATUS) 0xC0000223L)
+#endif
+
+#ifndef STATUS_PASSWORD_MUST_CHANGE
+# define STATUS_PASSWORD_MUST_CHANGE ((NTSTATUS) 0xC0000224L)
+#endif
+
+#ifndef STATUS_NOT_FOUND
+# define STATUS_NOT_FOUND ((NTSTATUS) 0xC0000225L)
+#endif
+
+#ifndef STATUS_NOT_TINY_STREAM
+# define STATUS_NOT_TINY_STREAM ((NTSTATUS) 0xC0000226L)
+#endif
+
+#ifndef STATUS_RECOVERY_FAILURE
+# define STATUS_RECOVERY_FAILURE ((NTSTATUS) 0xC0000227L)
+#endif
+
+#ifndef STATUS_STACK_OVERFLOW_READ
+# define STATUS_STACK_OVERFLOW_READ ((NTSTATUS) 0xC0000228L)
+#endif
+
+#ifndef STATUS_FAIL_CHECK
+# define STATUS_FAIL_CHECK ((NTSTATUS) 0xC0000229L)
+#endif
+
+#ifndef STATUS_DUPLICATE_OBJECTID
+# define STATUS_DUPLICATE_OBJECTID ((NTSTATUS) 0xC000022AL)
+#endif
+
+#ifndef STATUS_OBJECTID_EXISTS
+# define STATUS_OBJECTID_EXISTS ((NTSTATUS) 0xC000022BL)
+#endif
+
+#ifndef STATUS_CONVERT_TO_LARGE
+# define STATUS_CONVERT_TO_LARGE ((NTSTATUS) 0xC000022CL)
+#endif
+
+#ifndef STATUS_RETRY
+# define STATUS_RETRY ((NTSTATUS) 0xC000022DL)
+#endif
+
+#ifndef STATUS_FOUND_OUT_OF_SCOPE
+# define STATUS_FOUND_OUT_OF_SCOPE ((NTSTATUS) 0xC000022EL)
+#endif
+
+#ifndef STATUS_ALLOCATE_BUCKET
+# define STATUS_ALLOCATE_BUCKET ((NTSTATUS) 0xC000022FL)
+#endif
+
+#ifndef STATUS_PROPSET_NOT_FOUND
+# define STATUS_PROPSET_NOT_FOUND ((NTSTATUS) 0xC0000230L)
+#endif
+
+#ifndef STATUS_MARSHALL_OVERFLOW
+# define STATUS_MARSHALL_OVERFLOW ((NTSTATUS) 0xC0000231L)
+#endif
+
+#ifndef STATUS_INVALID_VARIANT
+# define STATUS_INVALID_VARIANT ((NTSTATUS) 0xC0000232L)
+#endif
+
+#ifndef STATUS_DOMAIN_CONTROLLER_NOT_FOUND
+# define STATUS_DOMAIN_CONTROLLER_NOT_FOUND ((NTSTATUS) 0xC0000233L)
+#endif
+
+#ifndef STATUS_ACCOUNT_LOCKED_OUT
+# define STATUS_ACCOUNT_LOCKED_OUT ((NTSTATUS) 0xC0000234L)
+#endif
+
+#ifndef STATUS_HANDLE_NOT_CLOSABLE
+# define STATUS_HANDLE_NOT_CLOSABLE ((NTSTATUS) 0xC0000235L)
+#endif
+
+#ifndef STATUS_CONNECTION_REFUSED
+# define STATUS_CONNECTION_REFUSED ((NTSTATUS) 0xC0000236L)
+#endif
+
+#ifndef STATUS_GRACEFUL_DISCONNECT
+# define STATUS_GRACEFUL_DISCONNECT ((NTSTATUS) 0xC0000237L)
+#endif
+
+#ifndef STATUS_ADDRESS_ALREADY_ASSOCIATED
+# define STATUS_ADDRESS_ALREADY_ASSOCIATED ((NTSTATUS) 0xC0000238L)
+#endif
+
+#ifndef STATUS_ADDRESS_NOT_ASSOCIATED
+# define STATUS_ADDRESS_NOT_ASSOCIATED ((NTSTATUS) 0xC0000239L)
+#endif
+
+#ifndef STATUS_CONNECTION_INVALID
+# define STATUS_CONNECTION_INVALID ((NTSTATUS) 0xC000023AL)
+#endif
+
+#ifndef STATUS_CONNECTION_ACTIVE
+# define STATUS_CONNECTION_ACTIVE ((NTSTATUS) 0xC000023BL)
+#endif
+
+#ifndef STATUS_NETWORK_UNREACHABLE
+# define STATUS_NETWORK_UNREACHABLE ((NTSTATUS) 0xC000023CL)
+#endif
+
+#ifndef STATUS_HOST_UNREACHABLE
+# define STATUS_HOST_UNREACHABLE ((NTSTATUS) 0xC000023DL)
+#endif
+
+#ifndef STATUS_PROTOCOL_UNREACHABLE
+# define STATUS_PROTOCOL_UNREACHABLE ((NTSTATUS) 0xC000023EL)
+#endif
+
+#ifndef STATUS_PORT_UNREACHABLE
+# define STATUS_PORT_UNREACHABLE ((NTSTATUS) 0xC000023FL)
+#endif
+
+#ifndef STATUS_REQUEST_ABORTED
+# define STATUS_REQUEST_ABORTED ((NTSTATUS) 0xC0000240L)
+#endif
+
+#ifndef STATUS_CONNECTION_ABORTED
+# define STATUS_CONNECTION_ABORTED ((NTSTATUS) 0xC0000241L)
+#endif
+
+#ifndef STATUS_BAD_COMPRESSION_BUFFER
+# define STATUS_BAD_COMPRESSION_BUFFER ((NTSTATUS) 0xC0000242L)
+#endif
+
+#ifndef STATUS_USER_MAPPED_FILE
+# define STATUS_USER_MAPPED_FILE ((NTSTATUS) 0xC0000243L)
+#endif
+
+#ifndef STATUS_AUDIT_FAILED
+# define STATUS_AUDIT_FAILED ((NTSTATUS) 0xC0000244L)
+#endif
+
+#ifndef STATUS_TIMER_RESOLUTION_NOT_SET
+# define STATUS_TIMER_RESOLUTION_NOT_SET ((NTSTATUS) 0xC0000245L)
+#endif
+
+#ifndef STATUS_CONNECTION_COUNT_LIMIT
+# define STATUS_CONNECTION_COUNT_LIMIT ((NTSTATUS) 0xC0000246L)
+#endif
+
+#ifndef STATUS_LOGIN_TIME_RESTRICTION
+# define STATUS_LOGIN_TIME_RESTRICTION ((NTSTATUS) 0xC0000247L)
+#endif
+
+#ifndef STATUS_LOGIN_WKSTA_RESTRICTION
+# define STATUS_LOGIN_WKSTA_RESTRICTION ((NTSTATUS) 0xC0000248L)
+#endif
+
+#ifndef STATUS_IMAGE_MP_UP_MISMATCH
+# define STATUS_IMAGE_MP_UP_MISMATCH ((NTSTATUS) 0xC0000249L)
+#endif
+
+#ifndef STATUS_INSUFFICIENT_LOGON_INFO
+# define STATUS_INSUFFICIENT_LOGON_INFO ((NTSTATUS) 0xC0000250L)
+#endif
+
+#ifndef STATUS_BAD_DLL_ENTRYPOINT
+# define STATUS_BAD_DLL_ENTRYPOINT ((NTSTATUS) 0xC0000251L)
+#endif
+
+#ifndef STATUS_BAD_SERVICE_ENTRYPOINT
+# define STATUS_BAD_SERVICE_ENTRYPOINT ((NTSTATUS) 0xC0000252L)
+#endif
+
+#ifndef STATUS_LPC_REPLY_LOST
+# define STATUS_LPC_REPLY_LOST ((NTSTATUS) 0xC0000253L)
+#endif
+
+#ifndef STATUS_IP_ADDRESS_CONFLICT1
+# define STATUS_IP_ADDRESS_CONFLICT1 ((NTSTATUS) 0xC0000254L)
+#endif
+
+#ifndef STATUS_IP_ADDRESS_CONFLICT2
+# define STATUS_IP_ADDRESS_CONFLICT2 ((NTSTATUS) 0xC0000255L)
+#endif
+
+#ifndef STATUS_REGISTRY_QUOTA_LIMIT
+# define STATUS_REGISTRY_QUOTA_LIMIT ((NTSTATUS) 0xC0000256L)
+#endif
+
+#ifndef STATUS_PATH_NOT_COVERED
+# define STATUS_PATH_NOT_COVERED ((NTSTATUS) 0xC0000257L)
+#endif
+
+#ifndef STATUS_NO_CALLBACK_ACTIVE
+# define STATUS_NO_CALLBACK_ACTIVE ((NTSTATUS) 0xC0000258L)
+#endif
+
+#ifndef STATUS_LICENSE_QUOTA_EXCEEDED
+# define STATUS_LICENSE_QUOTA_EXCEEDED ((NTSTATUS) 0xC0000259L)
+#endif
+
+#ifndef STATUS_PWD_TOO_SHORT
+# define STATUS_PWD_TOO_SHORT ((NTSTATUS) 0xC000025AL)
+#endif
+
+#ifndef STATUS_PWD_TOO_RECENT
+# define STATUS_PWD_TOO_RECENT ((NTSTATUS) 0xC000025BL)
+#endif
+
+#ifndef STATUS_PWD_HISTORY_CONFLICT
+# define STATUS_PWD_HISTORY_CONFLICT ((NTSTATUS) 0xC000025CL)
+#endif
+
+#ifndef STATUS_PLUGPLAY_NO_DEVICE
+# define STATUS_PLUGPLAY_NO_DEVICE ((NTSTATUS) 0xC000025EL)
+#endif
+
+#ifndef STATUS_UNSUPPORTED_COMPRESSION
+# define STATUS_UNSUPPORTED_COMPRESSION ((NTSTATUS) 0xC000025FL)
+#endif
+
+#ifndef STATUS_INVALID_HW_PROFILE
+# define STATUS_INVALID_HW_PROFILE ((NTSTATUS) 0xC0000260L)
+#endif
+
+#ifndef STATUS_INVALID_PLUGPLAY_DEVICE_PATH
+# define STATUS_INVALID_PLUGPLAY_DEVICE_PATH ((NTSTATUS) 0xC0000261L)
+#endif
+
+#ifndef STATUS_DRIVER_ORDINAL_NOT_FOUND
+# define STATUS_DRIVER_ORDINAL_NOT_FOUND ((NTSTATUS) 0xC0000262L)
+#endif
+
+#ifndef STATUS_DRIVER_ENTRYPOINT_NOT_FOUND
+# define STATUS_DRIVER_ENTRYPOINT_NOT_FOUND ((NTSTATUS) 0xC0000263L)
+#endif
+
+#ifndef STATUS_RESOURCE_NOT_OWNED
+# define STATUS_RESOURCE_NOT_OWNED ((NTSTATUS) 0xC0000264L)
+#endif
+
+#ifndef STATUS_TOO_MANY_LINKS
+# define STATUS_TOO_MANY_LINKS ((NTSTATUS) 0xC0000265L)
+#endif
+
+#ifndef STATUS_QUOTA_LIST_INCONSISTENT
+# define STATUS_QUOTA_LIST_INCONSISTENT ((NTSTATUS) 0xC0000266L)
+#endif
+
+#ifndef STATUS_FILE_IS_OFFLINE
+# define STATUS_FILE_IS_OFFLINE ((NTSTATUS) 0xC0000267L)
+#endif
+
+#ifndef STATUS_EVALUATION_EXPIRATION
+# define STATUS_EVALUATION_EXPIRATION ((NTSTATUS) 0xC0000268L)
+#endif
+
+#ifndef STATUS_ILLEGAL_DLL_RELOCATION
+# define STATUS_ILLEGAL_DLL_RELOCATION ((NTSTATUS) 0xC0000269L)
+#endif
+
+#ifndef STATUS_LICENSE_VIOLATION
+# define STATUS_LICENSE_VIOLATION ((NTSTATUS) 0xC000026AL)
+#endif
+
+#ifndef STATUS_DLL_INIT_FAILED_LOGOFF
+# define STATUS_DLL_INIT_FAILED_LOGOFF ((NTSTATUS) 0xC000026BL)
+#endif
+
+#ifndef STATUS_DRIVER_UNABLE_TO_LOAD
+# define STATUS_DRIVER_UNABLE_TO_LOAD ((NTSTATUS) 0xC000026CL)
+#endif
+
+#ifndef STATUS_DFS_UNAVAILABLE
+# define STATUS_DFS_UNAVAILABLE ((NTSTATUS) 0xC000026DL)
+#endif
+
+#ifndef STATUS_VOLUME_DISMOUNTED
+# define STATUS_VOLUME_DISMOUNTED ((NTSTATUS) 0xC000026EL)
+#endif
+
+#ifndef STATUS_WX86_INTERNAL_ERROR
+# define STATUS_WX86_INTERNAL_ERROR ((NTSTATUS) 0xC000026FL)
+#endif
+
+#ifndef STATUS_WX86_FLOAT_STACK_CHECK
+# define STATUS_WX86_FLOAT_STACK_CHECK ((NTSTATUS) 0xC0000270L)
+#endif
+
+#ifndef STATUS_VALIDATE_CONTINUE
+# define STATUS_VALIDATE_CONTINUE ((NTSTATUS) 0xC0000271L)
+#endif
+
+#ifndef STATUS_NO_MATCH
+# define STATUS_NO_MATCH ((NTSTATUS) 0xC0000272L)
+#endif
+
+#ifndef STATUS_NO_MORE_MATCHES
+# define STATUS_NO_MORE_MATCHES ((NTSTATUS) 0xC0000273L)
+#endif
+
+#ifndef STATUS_NOT_A_REPARSE_POINT
+# define STATUS_NOT_A_REPARSE_POINT ((NTSTATUS) 0xC0000275L)
+#endif
+
+#ifndef STATUS_IO_REPARSE_TAG_INVALID
+# define STATUS_IO_REPARSE_TAG_INVALID ((NTSTATUS) 0xC0000276L)
+#endif
+
+#ifndef STATUS_IO_REPARSE_TAG_MISMATCH
+# define STATUS_IO_REPARSE_TAG_MISMATCH ((NTSTATUS) 0xC0000277L)
+#endif
+
+#ifndef STATUS_IO_REPARSE_DATA_INVALID
+# define STATUS_IO_REPARSE_DATA_INVALID ((NTSTATUS) 0xC0000278L)
+#endif
+
+#ifndef STATUS_IO_REPARSE_TAG_NOT_HANDLED
+# define STATUS_IO_REPARSE_TAG_NOT_HANDLED ((NTSTATUS) 0xC0000279L)
+#endif
+
+#ifndef STATUS_REPARSE_POINT_NOT_RESOLVED
+# define STATUS_REPARSE_POINT_NOT_RESOLVED ((NTSTATUS) 0xC0000280L)
+#endif
+
+#ifndef STATUS_DIRECTORY_IS_A_REPARSE_POINT
+# define STATUS_DIRECTORY_IS_A_REPARSE_POINT ((NTSTATUS) 0xC0000281L)
+#endif
+
+#ifndef STATUS_RANGE_LIST_CONFLICT
+# define STATUS_RANGE_LIST_CONFLICT ((NTSTATUS) 0xC0000282L)
+#endif
+
+#ifndef STATUS_SOURCE_ELEMENT_EMPTY
+# define STATUS_SOURCE_ELEMENT_EMPTY ((NTSTATUS) 0xC0000283L)
+#endif
+
+#ifndef STATUS_DESTINATION_ELEMENT_FULL
+# define STATUS_DESTINATION_ELEMENT_FULL ((NTSTATUS) 0xC0000284L)
+#endif
+
+#ifndef STATUS_ILLEGAL_ELEMENT_ADDRESS
+# define STATUS_ILLEGAL_ELEMENT_ADDRESS ((NTSTATUS) 0xC0000285L)
+#endif
+
+#ifndef STATUS_MAGAZINE_NOT_PRESENT
+# define STATUS_MAGAZINE_NOT_PRESENT ((NTSTATUS) 0xC0000286L)
+#endif
+
+#ifndef STATUS_REINITIALIZATION_NEEDED
+# define STATUS_REINITIALIZATION_NEEDED ((NTSTATUS) 0xC0000287L)
+#endif
+
+#ifndef STATUS_DEVICE_REQUIRES_CLEANING
+# define STATUS_DEVICE_REQUIRES_CLEANING ((NTSTATUS) 0x80000288L)
+#endif
+
+#ifndef STATUS_DEVICE_DOOR_OPEN
+# define STATUS_DEVICE_DOOR_OPEN ((NTSTATUS) 0x80000289L)
+#endif
+
+#ifndef STATUS_ENCRYPTION_FAILED
+# define STATUS_ENCRYPTION_FAILED ((NTSTATUS) 0xC000028AL)
+#endif
+
+#ifndef STATUS_DECRYPTION_FAILED
+# define STATUS_DECRYPTION_FAILED ((NTSTATUS) 0xC000028BL)
+#endif
+
+#ifndef STATUS_RANGE_NOT_FOUND
+# define STATUS_RANGE_NOT_FOUND ((NTSTATUS) 0xC000028CL)
+#endif
+
+#ifndef STATUS_NO_RECOVERY_POLICY
+# define STATUS_NO_RECOVERY_POLICY ((NTSTATUS) 0xC000028DL)
+#endif
+
+#ifndef STATUS_NO_EFS
+# define STATUS_NO_EFS ((NTSTATUS) 0xC000028EL)
+#endif
+
+#ifndef STATUS_WRONG_EFS
+# define STATUS_WRONG_EFS ((NTSTATUS) 0xC000028FL)
+#endif
+
+#ifndef STATUS_NO_USER_KEYS
+# define STATUS_NO_USER_KEYS ((NTSTATUS) 0xC0000290L)
+#endif
+
+#ifndef STATUS_FILE_NOT_ENCRYPTED
+# define STATUS_FILE_NOT_ENCRYPTED ((NTSTATUS) 0xC0000291L)
+#endif
+
+#ifndef STATUS_NOT_EXPORT_FORMAT
+# define STATUS_NOT_EXPORT_FORMAT ((NTSTATUS) 0xC0000292L)
+#endif
+
+#ifndef STATUS_FILE_ENCRYPTED
+# define STATUS_FILE_ENCRYPTED ((NTSTATUS) 0xC0000293L)
+#endif
+
+#ifndef STATUS_WAKE_SYSTEM
+# define STATUS_WAKE_SYSTEM ((NTSTATUS) 0x40000294L)
+#endif
+
+#ifndef STATUS_WMI_GUID_NOT_FOUND
+# define STATUS_WMI_GUID_NOT_FOUND ((NTSTATUS) 0xC0000295L)
+#endif
+
+#ifndef STATUS_WMI_INSTANCE_NOT_FOUND
+# define STATUS_WMI_INSTANCE_NOT_FOUND ((NTSTATUS) 0xC0000296L)
+#endif
+
+#ifndef STATUS_WMI_ITEMID_NOT_FOUND
+# define STATUS_WMI_ITEMID_NOT_FOUND ((NTSTATUS) 0xC0000297L)
+#endif
+
+#ifndef STATUS_WMI_TRY_AGAIN
+# define STATUS_WMI_TRY_AGAIN ((NTSTATUS) 0xC0000298L)
+#endif
+
+#ifndef STATUS_SHARED_POLICY
+# define STATUS_SHARED_POLICY ((NTSTATUS) 0xC0000299L)
+#endif
+
+#ifndef STATUS_POLICY_OBJECT_NOT_FOUND
+# define STATUS_POLICY_OBJECT_NOT_FOUND ((NTSTATUS) 0xC000029AL)
+#endif
+
+#ifndef STATUS_POLICY_ONLY_IN_DS
+# define STATUS_POLICY_ONLY_IN_DS ((NTSTATUS) 0xC000029BL)
+#endif
+
+#ifndef STATUS_VOLUME_NOT_UPGRADED
+# define STATUS_VOLUME_NOT_UPGRADED ((NTSTATUS) 0xC000029CL)
+#endif
+
+#ifndef STATUS_REMOTE_STORAGE_NOT_ACTIVE
+# define STATUS_REMOTE_STORAGE_NOT_ACTIVE ((NTSTATUS) 0xC000029DL)
+#endif
+
+#ifndef STATUS_REMOTE_STORAGE_MEDIA_ERROR
+# define STATUS_REMOTE_STORAGE_MEDIA_ERROR ((NTSTATUS) 0xC000029EL)
+#endif
+
+#ifndef STATUS_NO_TRACKING_SERVICE
+# define STATUS_NO_TRACKING_SERVICE ((NTSTATUS) 0xC000029FL)
+#endif
+
+#ifndef STATUS_SERVER_SID_MISMATCH
+# define STATUS_SERVER_SID_MISMATCH ((NTSTATUS) 0xC00002A0L)
+#endif
+
+#ifndef STATUS_DS_NO_ATTRIBUTE_OR_VALUE
+# define STATUS_DS_NO_ATTRIBUTE_OR_VALUE ((NTSTATUS) 0xC00002A1L)
+#endif
+
+#ifndef STATUS_DS_INVALID_ATTRIBUTE_SYNTAX
+# define STATUS_DS_INVALID_ATTRIBUTE_SYNTAX ((NTSTATUS) 0xC00002A2L)
+#endif
+
+#ifndef STATUS_DS_ATTRIBUTE_TYPE_UNDEFINED
+# define STATUS_DS_ATTRIBUTE_TYPE_UNDEFINED ((NTSTATUS) 0xC00002A3L)
+#endif
+
+#ifndef STATUS_DS_ATTRIBUTE_OR_VALUE_EXISTS
+# define STATUS_DS_ATTRIBUTE_OR_VALUE_EXISTS ((NTSTATUS) 0xC00002A4L)
+#endif
+
+#ifndef STATUS_DS_BUSY
+# define STATUS_DS_BUSY ((NTSTATUS) 0xC00002A5L)
+#endif
+
+#ifndef STATUS_DS_UNAVAILABLE
+# define STATUS_DS_UNAVAILABLE ((NTSTATUS) 0xC00002A6L)
+#endif
+
+#ifndef STATUS_DS_NO_RIDS_ALLOCATED
+# define STATUS_DS_NO_RIDS_ALLOCATED ((NTSTATUS) 0xC00002A7L)
+#endif
+
+#ifndef STATUS_DS_NO_MORE_RIDS
+# define STATUS_DS_NO_MORE_RIDS ((NTSTATUS) 0xC00002A8L)
+#endif
+
+#ifndef STATUS_DS_INCORRECT_ROLE_OWNER
+# define STATUS_DS_INCORRECT_ROLE_OWNER ((NTSTATUS) 0xC00002A9L)
+#endif
+
+#ifndef STATUS_DS_RIDMGR_INIT_ERROR
+# define STATUS_DS_RIDMGR_INIT_ERROR ((NTSTATUS) 0xC00002AAL)
+#endif
+
+#ifndef STATUS_DS_OBJ_CLASS_VIOLATION
+# define STATUS_DS_OBJ_CLASS_VIOLATION ((NTSTATUS) 0xC00002ABL)
+#endif
+
+#ifndef STATUS_DS_CANT_ON_NON_LEAF
+# define STATUS_DS_CANT_ON_NON_LEAF ((NTSTATUS) 0xC00002ACL)
+#endif
+
+#ifndef STATUS_DS_CANT_ON_RDN
+# define STATUS_DS_CANT_ON_RDN ((NTSTATUS) 0xC00002ADL)
+#endif
+
+#ifndef STATUS_DS_CANT_MOD_OBJ_CLASS
+# define STATUS_DS_CANT_MOD_OBJ_CLASS ((NTSTATUS) 0xC00002AEL)
+#endif
+
+#ifndef STATUS_DS_CROSS_DOM_MOVE_FAILED
+# define STATUS_DS_CROSS_DOM_MOVE_FAILED ((NTSTATUS) 0xC00002AFL)
+#endif
+
+#ifndef STATUS_DS_GC_NOT_AVAILABLE
+# define STATUS_DS_GC_NOT_AVAILABLE ((NTSTATUS) 0xC00002B0L)
+#endif
+
+#ifndef STATUS_DIRECTORY_SERVICE_REQUIRED
+# define STATUS_DIRECTORY_SERVICE_REQUIRED ((NTSTATUS) 0xC00002B1L)
+#endif
+
+#ifndef STATUS_REPARSE_ATTRIBUTE_CONFLICT
+# define STATUS_REPARSE_ATTRIBUTE_CONFLICT ((NTSTATUS) 0xC00002B2L)
+#endif
+
+#ifndef STATUS_CANT_ENABLE_DENY_ONLY
+# define STATUS_CANT_ENABLE_DENY_ONLY ((NTSTATUS) 0xC00002B3L)
+#endif
+
+#ifndef STATUS_FLOAT_MULTIPLE_FAULTS
+# define STATUS_FLOAT_MULTIPLE_FAULTS ((NTSTATUS) 0xC00002B4L)
+#endif
+
+#ifndef STATUS_FLOAT_MULTIPLE_TRAPS
+# define STATUS_FLOAT_MULTIPLE_TRAPS ((NTSTATUS) 0xC00002B5L)
+#endif
+
+#ifndef STATUS_DEVICE_REMOVED
+# define STATUS_DEVICE_REMOVED ((NTSTATUS) 0xC00002B6L)
+#endif
+
+#ifndef STATUS_JOURNAL_DELETE_IN_PROGRESS
+# define STATUS_JOURNAL_DELETE_IN_PROGRESS ((NTSTATUS) 0xC00002B7L)
+#endif
+
+#ifndef STATUS_JOURNAL_NOT_ACTIVE
+# define STATUS_JOURNAL_NOT_ACTIVE ((NTSTATUS) 0xC00002B8L)
+#endif
+
+#ifndef STATUS_NOINTERFACE
+# define STATUS_NOINTERFACE ((NTSTATUS) 0xC00002B9L)
+#endif
+
+#ifndef STATUS_DS_ADMIN_LIMIT_EXCEEDED
+# define STATUS_DS_ADMIN_LIMIT_EXCEEDED ((NTSTATUS) 0xC00002C1L)
+#endif
+
+#ifndef STATUS_DRIVER_FAILED_SLEEP
+# define STATUS_DRIVER_FAILED_SLEEP ((NTSTATUS) 0xC00002C2L)
+#endif
+
+#ifndef STATUS_MUTUAL_AUTHENTICATION_FAILED
+# define STATUS_MUTUAL_AUTHENTICATION_FAILED ((NTSTATUS) 0xC00002C3L)
+#endif
+
+#ifndef STATUS_CORRUPT_SYSTEM_FILE
+# define STATUS_CORRUPT_SYSTEM_FILE ((NTSTATUS) 0xC00002C4L)
+#endif
+
+#ifndef STATUS_DATATYPE_MISALIGNMENT_ERROR
+# define STATUS_DATATYPE_MISALIGNMENT_ERROR ((NTSTATUS) 0xC00002C5L)
+#endif
+
+#ifndef STATUS_WMI_READ_ONLY
+# define STATUS_WMI_READ_ONLY ((NTSTATUS) 0xC00002C6L)
+#endif
+
+#ifndef STATUS_WMI_SET_FAILURE
+# define STATUS_WMI_SET_FAILURE ((NTSTATUS) 0xC00002C7L)
+#endif
+
+#ifndef STATUS_COMMITMENT_MINIMUM
+# define STATUS_COMMITMENT_MINIMUM ((NTSTATUS) 0xC00002C8L)
+#endif
+
+#ifndef STATUS_REG_NAT_CONSUMPTION
+# define STATUS_REG_NAT_CONSUMPTION ((NTSTATUS) 0xC00002C9L)
+#endif
+
+#ifndef STATUS_TRANSPORT_FULL
+# define STATUS_TRANSPORT_FULL ((NTSTATUS) 0xC00002CAL)
+#endif
+
+#ifndef STATUS_DS_SAM_INIT_FAILURE
+# define STATUS_DS_SAM_INIT_FAILURE ((NTSTATUS) 0xC00002CBL)
+#endif
+
+#ifndef STATUS_ONLY_IF_CONNECTED
+# define STATUS_ONLY_IF_CONNECTED ((NTSTATUS) 0xC00002CCL)
+#endif
+
+#ifndef STATUS_DS_SENSITIVE_GROUP_VIOLATION
+# define STATUS_DS_SENSITIVE_GROUP_VIOLATION ((NTSTATUS) 0xC00002CDL)
+#endif
+
+#ifndef STATUS_PNP_RESTART_ENUMERATION
+# define STATUS_PNP_RESTART_ENUMERATION ((NTSTATUS) 0xC00002CEL)
+#endif
+
+#ifndef STATUS_JOURNAL_ENTRY_DELETED
+# define STATUS_JOURNAL_ENTRY_DELETED ((NTSTATUS) 0xC00002CFL)
+#endif
+
+#ifndef STATUS_DS_CANT_MOD_PRIMARYGROUPID
+# define STATUS_DS_CANT_MOD_PRIMARYGROUPID ((NTSTATUS) 0xC00002D0L)
+#endif
+
+#ifndef STATUS_SYSTEM_IMAGE_BAD_SIGNATURE
+# define STATUS_SYSTEM_IMAGE_BAD_SIGNATURE ((NTSTATUS) 0xC00002D1L)
+#endif
+
+#ifndef STATUS_PNP_REBOOT_REQUIRED
+# define STATUS_PNP_REBOOT_REQUIRED ((NTSTATUS) 0xC00002D2L)
+#endif
+
+#ifndef STATUS_POWER_STATE_INVALID
+# define STATUS_POWER_STATE_INVALID ((NTSTATUS) 0xC00002D3L)
+#endif
+
+#ifndef STATUS_DS_INVALID_GROUP_TYPE
+# define STATUS_DS_INVALID_GROUP_TYPE ((NTSTATUS) 0xC00002D4L)
+#endif
+
+#ifndef STATUS_DS_NO_NEST_GLOBALGROUP_IN_MIXEDDOMAIN
+# define STATUS_DS_NO_NEST_GLOBALGROUP_IN_MIXEDDOMAIN ((NTSTATUS) 0xC00002D5L)
+#endif
+
+#ifndef STATUS_DS_NO_NEST_LOCALGROUP_IN_MIXEDDOMAIN
+# define STATUS_DS_NO_NEST_LOCALGROUP_IN_MIXEDDOMAIN ((NTSTATUS) 0xC00002D6L)
+#endif
+
+#ifndef STATUS_DS_GLOBAL_CANT_HAVE_LOCAL_MEMBER
+# define STATUS_DS_GLOBAL_CANT_HAVE_LOCAL_MEMBER ((NTSTATUS) 0xC00002D7L)
+#endif
+
+#ifndef STATUS_DS_GLOBAL_CANT_HAVE_UNIVERSAL_MEMBER
+# define STATUS_DS_GLOBAL_CANT_HAVE_UNIVERSAL_MEMBER ((NTSTATUS) 0xC00002D8L)
+#endif
+
+#ifndef STATUS_DS_UNIVERSAL_CANT_HAVE_LOCAL_MEMBER
+# define STATUS_DS_UNIVERSAL_CANT_HAVE_LOCAL_MEMBER ((NTSTATUS) 0xC00002D9L)
+#endif
+
+#ifndef STATUS_DS_GLOBAL_CANT_HAVE_CROSSDOMAIN_MEMBER
+# define STATUS_DS_GLOBAL_CANT_HAVE_CROSSDOMAIN_MEMBER ((NTSTATUS) 0xC00002DAL)
+#endif
+
+#ifndef STATUS_DS_LOCAL_CANT_HAVE_CROSSDOMAIN_LOCAL_MEMBER
+# define STATUS_DS_LOCAL_CANT_HAVE_CROSSDOMAIN_LOCAL_MEMBER ((NTSTATUS) 0xC00002DBL)
+#endif
+
+#ifndef STATUS_DS_HAVE_PRIMARY_MEMBERS
+# define STATUS_DS_HAVE_PRIMARY_MEMBERS ((NTSTATUS) 0xC00002DCL)
+#endif
+
+#ifndef STATUS_WMI_NOT_SUPPORTED
+# define STATUS_WMI_NOT_SUPPORTED ((NTSTATUS) 0xC00002DDL)
+#endif
+
+#ifndef STATUS_INSUFFICIENT_POWER
+# define STATUS_INSUFFICIENT_POWER ((NTSTATUS) 0xC00002DEL)
+#endif
+
+#ifndef STATUS_SAM_NEED_BOOTKEY_PASSWORD
+# define STATUS_SAM_NEED_BOOTKEY_PASSWORD ((NTSTATUS) 0xC00002DFL)
+#endif
+
+#ifndef STATUS_SAM_NEED_BOOTKEY_FLOPPY
+# define STATUS_SAM_NEED_BOOTKEY_FLOPPY ((NTSTATUS) 0xC00002E0L)
+#endif
+
+#ifndef STATUS_DS_CANT_START
+# define STATUS_DS_CANT_START ((NTSTATUS) 0xC00002E1L)
+#endif
+
+#ifndef STATUS_DS_INIT_FAILURE
+# define STATUS_DS_INIT_FAILURE ((NTSTATUS) 0xC00002E2L)
+#endif
+
+#ifndef STATUS_SAM_INIT_FAILURE
+# define STATUS_SAM_INIT_FAILURE ((NTSTATUS) 0xC00002E3L)
+#endif
+
+#ifndef STATUS_DS_GC_REQUIRED
+# define STATUS_DS_GC_REQUIRED ((NTSTATUS) 0xC00002E4L)
+#endif
+
+#ifndef STATUS_DS_LOCAL_MEMBER_OF_LOCAL_ONLY
+# define STATUS_DS_LOCAL_MEMBER_OF_LOCAL_ONLY ((NTSTATUS) 0xC00002E5L)
+#endif
+
+#ifndef STATUS_DS_NO_FPO_IN_UNIVERSAL_GROUPS
+# define STATUS_DS_NO_FPO_IN_UNIVERSAL_GROUPS ((NTSTATUS) 0xC00002E6L)
+#endif
+
+#ifndef STATUS_DS_MACHINE_ACCOUNT_QUOTA_EXCEEDED
+# define STATUS_DS_MACHINE_ACCOUNT_QUOTA_EXCEEDED ((NTSTATUS) 0xC00002E7L)
+#endif
+
+#ifndef STATUS_MULTIPLE_FAULT_VIOLATION
+# define STATUS_MULTIPLE_FAULT_VIOLATION ((NTSTATUS) 0xC00002E8L)
+#endif
+
+#ifndef STATUS_CURRENT_DOMAIN_NOT_ALLOWED
+# define STATUS_CURRENT_DOMAIN_NOT_ALLOWED ((NTSTATUS) 0xC00002E9L)
+#endif
+
+#ifndef STATUS_CANNOT_MAKE
+# define STATUS_CANNOT_MAKE ((NTSTATUS) 0xC00002EAL)
+#endif
+
+#ifndef STATUS_SYSTEM_SHUTDOWN
+# define STATUS_SYSTEM_SHUTDOWN ((NTSTATUS) 0xC00002EBL)
+#endif
+
+#ifndef STATUS_DS_INIT_FAILURE_CONSOLE
+# define STATUS_DS_INIT_FAILURE_CONSOLE ((NTSTATUS) 0xC00002ECL)
+#endif
+
+#ifndef STATUS_DS_SAM_INIT_FAILURE_CONSOLE
+# define STATUS_DS_SAM_INIT_FAILURE_CONSOLE ((NTSTATUS) 0xC00002EDL)
+#endif
+
+#ifndef STATUS_UNFINISHED_CONTEXT_DELETED
+# define STATUS_UNFINISHED_CONTEXT_DELETED ((NTSTATUS) 0xC00002EEL)
+#endif
+
+#ifndef STATUS_NO_TGT_REPLY
+# define STATUS_NO_TGT_REPLY ((NTSTATUS) 0xC00002EFL)
+#endif
+
+#ifndef STATUS_OBJECTID_NOT_FOUND
+# define STATUS_OBJECTID_NOT_FOUND ((NTSTATUS) 0xC00002F0L)
+#endif
+
+#ifndef STATUS_NO_IP_ADDRESSES
+# define STATUS_NO_IP_ADDRESSES ((NTSTATUS) 0xC00002F1L)
+#endif
+
+#ifndef STATUS_WRONG_CREDENTIAL_HANDLE
+# define STATUS_WRONG_CREDENTIAL_HANDLE ((NTSTATUS) 0xC00002F2L)
+#endif
+
+#ifndef STATUS_CRYPTO_SYSTEM_INVALID
+# define STATUS_CRYPTO_SYSTEM_INVALID ((NTSTATUS) 0xC00002F3L)
+#endif
+
+#ifndef STATUS_MAX_REFERRALS_EXCEEDED
+# define STATUS_MAX_REFERRALS_EXCEEDED ((NTSTATUS) 0xC00002F4L)
+#endif
+
+#ifndef STATUS_MUST_BE_KDC
+# define STATUS_MUST_BE_KDC ((NTSTATUS) 0xC00002F5L)
+#endif
+
+#ifndef STATUS_STRONG_CRYPTO_NOT_SUPPORTED
+# define STATUS_STRONG_CRYPTO_NOT_SUPPORTED ((NTSTATUS) 0xC00002F6L)
+#endif
+
+#ifndef STATUS_TOO_MANY_PRINCIPALS
+# define STATUS_TOO_MANY_PRINCIPALS ((NTSTATUS) 0xC00002F7L)
+#endif
+
+#ifndef STATUS_NO_PA_DATA
+# define STATUS_NO_PA_DATA ((NTSTATUS) 0xC00002F8L)
+#endif
+
+#ifndef STATUS_PKINIT_NAME_MISMATCH
+# define STATUS_PKINIT_NAME_MISMATCH ((NTSTATUS) 0xC00002F9L)
+#endif
+
+#ifndef STATUS_SMARTCARD_LOGON_REQUIRED
+# define STATUS_SMARTCARD_LOGON_REQUIRED ((NTSTATUS) 0xC00002FAL)
+#endif
+
+#ifndef STATUS_KDC_INVALID_REQUEST
+# define STATUS_KDC_INVALID_REQUEST ((NTSTATUS) 0xC00002FBL)
+#endif
+
+#ifndef STATUS_KDC_UNABLE_TO_REFER
+# define STATUS_KDC_UNABLE_TO_REFER ((NTSTATUS) 0xC00002FCL)
+#endif
+
+#ifndef STATUS_KDC_UNKNOWN_ETYPE
+# define STATUS_KDC_UNKNOWN_ETYPE ((NTSTATUS) 0xC00002FDL)
+#endif
+
+#ifndef STATUS_SHUTDOWN_IN_PROGRESS
+# define STATUS_SHUTDOWN_IN_PROGRESS ((NTSTATUS) 0xC00002FEL)
+#endif
+
+#ifndef STATUS_SERVER_SHUTDOWN_IN_PROGRESS
+# define STATUS_SERVER_SHUTDOWN_IN_PROGRESS ((NTSTATUS) 0xC00002FFL)
+#endif
+
+#ifndef STATUS_NOT_SUPPORTED_ON_SBS
+# define STATUS_NOT_SUPPORTED_ON_SBS ((NTSTATUS) 0xC0000300L)
+#endif
+
+#ifndef STATUS_WMI_GUID_DISCONNECTED
+# define STATUS_WMI_GUID_DISCONNECTED ((NTSTATUS) 0xC0000301L)
+#endif
+
+#ifndef STATUS_WMI_ALREADY_DISABLED
+# define STATUS_WMI_ALREADY_DISABLED ((NTSTATUS) 0xC0000302L)
+#endif
+
+#ifndef STATUS_WMI_ALREADY_ENABLED
+# define STATUS_WMI_ALREADY_ENABLED ((NTSTATUS) 0xC0000303L)
+#endif
+
+#ifndef STATUS_MFT_TOO_FRAGMENTED
+# define STATUS_MFT_TOO_FRAGMENTED ((NTSTATUS) 0xC0000304L)
+#endif
+
+#ifndef STATUS_COPY_PROTECTION_FAILURE
+# define STATUS_COPY_PROTECTION_FAILURE ((NTSTATUS) 0xC0000305L)
+#endif
+
+#ifndef STATUS_CSS_AUTHENTICATION_FAILURE
+# define STATUS_CSS_AUTHENTICATION_FAILURE ((NTSTATUS) 0xC0000306L)
+#endif
+
+#ifndef STATUS_CSS_KEY_NOT_PRESENT
+# define STATUS_CSS_KEY_NOT_PRESENT ((NTSTATUS) 0xC0000307L)
+#endif
+
+#ifndef STATUS_CSS_KEY_NOT_ESTABLISHED
+# define STATUS_CSS_KEY_NOT_ESTABLISHED ((NTSTATUS) 0xC0000308L)
+#endif
+
+#ifndef STATUS_CSS_SCRAMBLED_SECTOR
+# define STATUS_CSS_SCRAMBLED_SECTOR ((NTSTATUS) 0xC0000309L)
+#endif
+
+#ifndef STATUS_CSS_REGION_MISMATCH
+# define STATUS_CSS_REGION_MISMATCH ((NTSTATUS) 0xC000030AL)
+#endif
+
+#ifndef STATUS_CSS_RESETS_EXHAUSTED
+# define STATUS_CSS_RESETS_EXHAUSTED ((NTSTATUS) 0xC000030BL)
+#endif
+
+#ifndef STATUS_PKINIT_FAILURE
+# define STATUS_PKINIT_FAILURE ((NTSTATUS) 0xC0000320L)
+#endif
+
+#ifndef STATUS_SMARTCARD_SUBSYSTEM_FAILURE
+# define STATUS_SMARTCARD_SUBSYSTEM_FAILURE ((NTSTATUS) 0xC0000321L)
+#endif
+
+#ifndef STATUS_NO_KERB_KEY
+# define STATUS_NO_KERB_KEY ((NTSTATUS) 0xC0000322L)
+#endif
+
+#ifndef STATUS_HOST_DOWN
+# define STATUS_HOST_DOWN ((NTSTATUS) 0xC0000350L)
+#endif
+
+#ifndef STATUS_UNSUPPORTED_PREAUTH
+# define STATUS_UNSUPPORTED_PREAUTH ((NTSTATUS) 0xC0000351L)
+#endif
+
+#ifndef STATUS_EFS_ALG_BLOB_TOO_BIG
+# define STATUS_EFS_ALG_BLOB_TOO_BIG ((NTSTATUS) 0xC0000352L)
+#endif
+
+#ifndef STATUS_PORT_NOT_SET
+# define STATUS_PORT_NOT_SET ((NTSTATUS) 0xC0000353L)
+#endif
+
+#ifndef STATUS_DEBUGGER_INACTIVE
+# define STATUS_DEBUGGER_INACTIVE ((NTSTATUS) 0xC0000354L)
+#endif
+
+#ifndef STATUS_DS_VERSION_CHECK_FAILURE
+# define STATUS_DS_VERSION_CHECK_FAILURE ((NTSTATUS) 0xC0000355L)
+#endif
+
+#ifndef STATUS_AUDITING_DISABLED
+# define STATUS_AUDITING_DISABLED ((NTSTATUS) 0xC0000356L)
+#endif
+
+#ifndef STATUS_PRENT4_MACHINE_ACCOUNT
+# define STATUS_PRENT4_MACHINE_ACCOUNT ((NTSTATUS) 0xC0000357L)
+#endif
+
+#ifndef STATUS_DS_AG_CANT_HAVE_UNIVERSAL_MEMBER
+# define STATUS_DS_AG_CANT_HAVE_UNIVERSAL_MEMBER ((NTSTATUS) 0xC0000358L)
+#endif
+
+#ifndef STATUS_INVALID_IMAGE_WIN_32
+# define STATUS_INVALID_IMAGE_WIN_32 ((NTSTATUS) 0xC0000359L)
+#endif
+
+#ifndef STATUS_INVALID_IMAGE_WIN_64
+# define STATUS_INVALID_IMAGE_WIN_64 ((NTSTATUS) 0xC000035AL)
+#endif
+
+#ifndef STATUS_BAD_BINDINGS
+# define STATUS_BAD_BINDINGS ((NTSTATUS) 0xC000035BL)
+#endif
+
+#ifndef STATUS_NETWORK_SESSION_EXPIRED
+# define STATUS_NETWORK_SESSION_EXPIRED ((NTSTATUS) 0xC000035CL)
+#endif
+
+#ifndef STATUS_APPHELP_BLOCK
+# define STATUS_APPHELP_BLOCK ((NTSTATUS) 0xC000035DL)
+#endif
+
+#ifndef STATUS_ALL_SIDS_FILTERED
+# define STATUS_ALL_SIDS_FILTERED ((NTSTATUS) 0xC000035EL)
+#endif
+
+#ifndef STATUS_NOT_SAFE_MODE_DRIVER
+# define STATUS_NOT_SAFE_MODE_DRIVER ((NTSTATUS) 0xC000035FL)
+#endif
+
+#ifndef STATUS_ACCESS_DISABLED_BY_POLICY_DEFAULT
+# define STATUS_ACCESS_DISABLED_BY_POLICY_DEFAULT ((NTSTATUS) 0xC0000361L)
+#endif
+
+#ifndef STATUS_ACCESS_DISABLED_BY_POLICY_PATH
+# define STATUS_ACCESS_DISABLED_BY_POLICY_PATH ((NTSTATUS) 0xC0000362L)
+#endif
+
+#ifndef STATUS_ACCESS_DISABLED_BY_POLICY_PUBLISHER
+# define STATUS_ACCESS_DISABLED_BY_POLICY_PUBLISHER ((NTSTATUS) 0xC0000363L)
+#endif
+
+#ifndef STATUS_ACCESS_DISABLED_BY_POLICY_OTHER
+# define STATUS_ACCESS_DISABLED_BY_POLICY_OTHER ((NTSTATUS) 0xC0000364L)
+#endif
+
+#ifndef STATUS_FAILED_DRIVER_ENTRY
+# define STATUS_FAILED_DRIVER_ENTRY ((NTSTATUS) 0xC0000365L)
+#endif
+
+#ifndef STATUS_DEVICE_ENUMERATION_ERROR
+# define STATUS_DEVICE_ENUMERATION_ERROR ((NTSTATUS) 0xC0000366L)
+#endif
+
+#ifndef STATUS_MOUNT_POINT_NOT_RESOLVED
+# define STATUS_MOUNT_POINT_NOT_RESOLVED ((NTSTATUS) 0xC0000368L)
+#endif
+
+#ifndef STATUS_INVALID_DEVICE_OBJECT_PARAMETER
+# define STATUS_INVALID_DEVICE_OBJECT_PARAMETER ((NTSTATUS) 0xC0000369L)
+#endif
+
+#ifndef STATUS_MCA_OCCURED
+# define STATUS_MCA_OCCURED ((NTSTATUS) 0xC000036AL)
+#endif
+
+#ifndef STATUS_DRIVER_BLOCKED_CRITICAL
+# define STATUS_DRIVER_BLOCKED_CRITICAL ((NTSTATUS) 0xC000036BL)
+#endif
+
+#ifndef STATUS_DRIVER_BLOCKED
+# define STATUS_DRIVER_BLOCKED ((NTSTATUS) 0xC000036CL)
+#endif
+
+#ifndef STATUS_DRIVER_DATABASE_ERROR
+# define STATUS_DRIVER_DATABASE_ERROR ((NTSTATUS) 0xC000036DL)
+#endif
+
+#ifndef STATUS_SYSTEM_HIVE_TOO_LARGE
+# define STATUS_SYSTEM_HIVE_TOO_LARGE ((NTSTATUS) 0xC000036EL)
+#endif
+
+#ifndef STATUS_INVALID_IMPORT_OF_NON_DLL
+# define STATUS_INVALID_IMPORT_OF_NON_DLL ((NTSTATUS) 0xC000036FL)
+#endif
+
+#ifndef STATUS_DS_SHUTTING_DOWN
+# define STATUS_DS_SHUTTING_DOWN ((NTSTATUS) 0x40000370L)
+#endif
+
+#ifndef STATUS_NO_SECRETS
+# define STATUS_NO_SECRETS ((NTSTATUS) 0xC0000371L)
+#endif
+
+#ifndef STATUS_ACCESS_DISABLED_NO_SAFER_UI_BY_POLICY
+# define STATUS_ACCESS_DISABLED_NO_SAFER_UI_BY_POLICY ((NTSTATUS) 0xC0000372L)
+#endif
+
+#ifndef STATUS_FAILED_STACK_SWITCH
+# define STATUS_FAILED_STACK_SWITCH ((NTSTATUS) 0xC0000373L)
+#endif
+
+#ifndef STATUS_HEAP_CORRUPTION
+# define STATUS_HEAP_CORRUPTION ((NTSTATUS) 0xC0000374L)
+#endif
+
+#ifndef STATUS_SMARTCARD_WRONG_PIN
+# define STATUS_SMARTCARD_WRONG_PIN ((NTSTATUS) 0xC0000380L)
+#endif
+
+#ifndef STATUS_SMARTCARD_CARD_BLOCKED
+# define STATUS_SMARTCARD_CARD_BLOCKED ((NTSTATUS) 0xC0000381L)
+#endif
+
+#ifndef STATUS_SMARTCARD_CARD_NOT_AUTHENTICATED
+# define STATUS_SMARTCARD_CARD_NOT_AUTHENTICATED ((NTSTATUS) 0xC0000382L)
+#endif
+
+#ifndef STATUS_SMARTCARD_NO_CARD
+# define STATUS_SMARTCARD_NO_CARD ((NTSTATUS) 0xC0000383L)
+#endif
+
+#ifndef STATUS_SMARTCARD_NO_KEY_CONTAINER
+# define STATUS_SMARTCARD_NO_KEY_CONTAINER ((NTSTATUS) 0xC0000384L)
+#endif
+
+#ifndef STATUS_SMARTCARD_NO_CERTIFICATE
+# define STATUS_SMARTCARD_NO_CERTIFICATE ((NTSTATUS) 0xC0000385L)
+#endif
+
+#ifndef STATUS_SMARTCARD_NO_KEYSET
+# define STATUS_SMARTCARD_NO_KEYSET ((NTSTATUS) 0xC0000386L)
+#endif
+
+#ifndef STATUS_SMARTCARD_IO_ERROR
+# define STATUS_SMARTCARD_IO_ERROR ((NTSTATUS) 0xC0000387L)
+#endif
+
+#ifndef STATUS_DOWNGRADE_DETECTED
+# define STATUS_DOWNGRADE_DETECTED ((NTSTATUS) 0xC0000388L)
+#endif
+
+#ifndef STATUS_SMARTCARD_CERT_REVOKED
+# define STATUS_SMARTCARD_CERT_REVOKED ((NTSTATUS) 0xC0000389L)
+#endif
+
+#ifndef STATUS_ISSUING_CA_UNTRUSTED
+# define STATUS_ISSUING_CA_UNTRUSTED ((NTSTATUS) 0xC000038AL)
+#endif
+
+#ifndef STATUS_REVOCATION_OFFLINE_C
+# define STATUS_REVOCATION_OFFLINE_C ((NTSTATUS) 0xC000038BL)
+#endif
+
+#ifndef STATUS_PKINIT_CLIENT_FAILURE
+# define STATUS_PKINIT_CLIENT_FAILURE ((NTSTATUS) 0xC000038CL)
+#endif
+
+#ifndef STATUS_SMARTCARD_CERT_EXPIRED
+# define STATUS_SMARTCARD_CERT_EXPIRED ((NTSTATUS) 0xC000038DL)
+#endif
+
+#ifndef STATUS_DRIVER_FAILED_PRIOR_UNLOAD
+# define STATUS_DRIVER_FAILED_PRIOR_UNLOAD ((NTSTATUS) 0xC000038EL)
+#endif
+
+#ifndef STATUS_SMARTCARD_SILENT_CONTEXT
+# define STATUS_SMARTCARD_SILENT_CONTEXT ((NTSTATUS) 0xC000038FL)
+#endif
+
+#ifndef STATUS_PER_USER_TRUST_QUOTA_EXCEEDED
+# define STATUS_PER_USER_TRUST_QUOTA_EXCEEDED ((NTSTATUS) 0xC0000401L)
+#endif
+
+#ifndef STATUS_ALL_USER_TRUST_QUOTA_EXCEEDED
+# define STATUS_ALL_USER_TRUST_QUOTA_EXCEEDED ((NTSTATUS) 0xC0000402L)
+#endif
+
+#ifndef STATUS_USER_DELETE_TRUST_QUOTA_EXCEEDED
+# define STATUS_USER_DELETE_TRUST_QUOTA_EXCEEDED ((NTSTATUS) 0xC0000403L)
+#endif
+
+#ifndef STATUS_DS_NAME_NOT_UNIQUE
+# define STATUS_DS_NAME_NOT_UNIQUE ((NTSTATUS) 0xC0000404L)
+#endif
+
+#ifndef STATUS_DS_DUPLICATE_ID_FOUND
+# define STATUS_DS_DUPLICATE_ID_FOUND ((NTSTATUS) 0xC0000405L)
+#endif
+
+#ifndef STATUS_DS_GROUP_CONVERSION_ERROR
+# define STATUS_DS_GROUP_CONVERSION_ERROR ((NTSTATUS) 0xC0000406L)
+#endif
+
+#ifndef STATUS_VOLSNAP_PREPARE_HIBERNATE
+# define STATUS_VOLSNAP_PREPARE_HIBERNATE ((NTSTATUS) 0xC0000407L)
+#endif
+
+#ifndef STATUS_USER2USER_REQUIRED
+# define STATUS_USER2USER_REQUIRED ((NTSTATUS) 0xC0000408L)
+#endif
+
+#ifndef STATUS_STACK_BUFFER_OVERRUN
+# define STATUS_STACK_BUFFER_OVERRUN ((NTSTATUS) 0xC0000409L)
+#endif
+
+#ifndef STATUS_NO_S4U_PROT_SUPPORT
+# define STATUS_NO_S4U_PROT_SUPPORT ((NTSTATUS) 0xC000040AL)
+#endif
+
+#ifndef STATUS_CROSSREALM_DELEGATION_FAILURE
+# define STATUS_CROSSREALM_DELEGATION_FAILURE ((NTSTATUS) 0xC000040BL)
+#endif
+
+#ifndef STATUS_REVOCATION_OFFLINE_KDC
+# define STATUS_REVOCATION_OFFLINE_KDC ((NTSTATUS) 0xC000040CL)
+#endif
+
+#ifndef STATUS_ISSUING_CA_UNTRUSTED_KDC
+# define STATUS_ISSUING_CA_UNTRUSTED_KDC ((NTSTATUS) 0xC000040DL)
+#endif
+
+#ifndef STATUS_KDC_CERT_EXPIRED
+# define STATUS_KDC_CERT_EXPIRED ((NTSTATUS) 0xC000040EL)
+#endif
+
+#ifndef STATUS_KDC_CERT_REVOKED
+# define STATUS_KDC_CERT_REVOKED ((NTSTATUS) 0xC000040FL)
+#endif
+
+#ifndef STATUS_PARAMETER_QUOTA_EXCEEDED
+# define STATUS_PARAMETER_QUOTA_EXCEEDED ((NTSTATUS) 0xC0000410L)
+#endif
+
+#ifndef STATUS_HIBERNATION_FAILURE
+# define STATUS_HIBERNATION_FAILURE ((NTSTATUS) 0xC0000411L)
+#endif
+
+#ifndef STATUS_DELAY_LOAD_FAILED
+# define STATUS_DELAY_LOAD_FAILED ((NTSTATUS) 0xC0000412L)
+#endif
+
+#ifndef STATUS_AUTHENTICATION_FIREWALL_FAILED
+# define STATUS_AUTHENTICATION_FIREWALL_FAILED ((NTSTATUS) 0xC0000413L)
+#endif
+
+#ifndef STATUS_VDM_DISALLOWED
+# define STATUS_VDM_DISALLOWED ((NTSTATUS) 0xC0000414L)
+#endif
+
+#ifndef STATUS_HUNG_DISPLAY_DRIVER_THREAD
+# define STATUS_HUNG_DISPLAY_DRIVER_THREAD ((NTSTATUS) 0xC0000415L)
+#endif
+
+#ifndef STATUS_INSUFFICIENT_RESOURCE_FOR_SPECIFIED_SHARED_SECTION_SIZE
+# define STATUS_INSUFFICIENT_RESOURCE_FOR_SPECIFIED_SHARED_SECTION_SIZE ((NTSTATUS) 0xC0000416L)
+#endif
+
+#ifndef STATUS_INVALID_CRUNTIME_PARAMETER
+# define STATUS_INVALID_CRUNTIME_PARAMETER ((NTSTATUS) 0xC0000417L)
+#endif
+
+#ifndef STATUS_NTLM_BLOCKED
+# define STATUS_NTLM_BLOCKED ((NTSTATUS) 0xC0000418L)
+#endif
+
+#ifndef STATUS_DS_SRC_SID_EXISTS_IN_FOREST
+# define STATUS_DS_SRC_SID_EXISTS_IN_FOREST ((NTSTATUS) 0xC0000419L)
+#endif
+
+#ifndef STATUS_DS_DOMAIN_NAME_EXISTS_IN_FOREST
+# define STATUS_DS_DOMAIN_NAME_EXISTS_IN_FOREST ((NTSTATUS) 0xC000041AL)
+#endif
+
+#ifndef STATUS_DS_FLAT_NAME_EXISTS_IN_FOREST
+# define STATUS_DS_FLAT_NAME_EXISTS_IN_FOREST ((NTSTATUS) 0xC000041BL)
+#endif
+
+#ifndef STATUS_INVALID_USER_PRINCIPAL_NAME
+# define STATUS_INVALID_USER_PRINCIPAL_NAME ((NTSTATUS) 0xC000041CL)
+#endif
+
+#ifndef STATUS_FATAL_USER_CALLBACK_EXCEPTION
+# define STATUS_FATAL_USER_CALLBACK_EXCEPTION ((NTSTATUS) 0xC000041DL)
+#endif
+
+#ifndef STATUS_ASSERTION_FAILURE
+# define STATUS_ASSERTION_FAILURE ((NTSTATUS) 0xC0000420L)
+#endif
+
+#ifndef STATUS_VERIFIER_STOP
+# define STATUS_VERIFIER_STOP ((NTSTATUS) 0xC0000421L)
+#endif
+
+#ifndef STATUS_CALLBACK_POP_STACK
+# define STATUS_CALLBACK_POP_STACK ((NTSTATUS) 0xC0000423L)
+#endif
+
+#ifndef STATUS_INCOMPATIBLE_DRIVER_BLOCKED
+# define STATUS_INCOMPATIBLE_DRIVER_BLOCKED ((NTSTATUS) 0xC0000424L)
+#endif
+
+#ifndef STATUS_HIVE_UNLOADED
+# define STATUS_HIVE_UNLOADED ((NTSTATUS) 0xC0000425L)
+#endif
+
+#ifndef STATUS_COMPRESSION_DISABLED
+# define STATUS_COMPRESSION_DISABLED ((NTSTATUS) 0xC0000426L)
+#endif
+
+#ifndef STATUS_FILE_SYSTEM_LIMITATION
+# define STATUS_FILE_SYSTEM_LIMITATION ((NTSTATUS) 0xC0000427L)
+#endif
+
+#ifndef STATUS_INVALID_IMAGE_HASH
+# define STATUS_INVALID_IMAGE_HASH ((NTSTATUS) 0xC0000428L)
+#endif
+
+#ifndef STATUS_NOT_CAPABLE
+# define STATUS_NOT_CAPABLE ((NTSTATUS) 0xC0000429L)
+#endif
+
+#ifndef STATUS_REQUEST_OUT_OF_SEQUENCE
+# define STATUS_REQUEST_OUT_OF_SEQUENCE ((NTSTATUS) 0xC000042AL)
+#endif
+
+#ifndef STATUS_IMPLEMENTATION_LIMIT
+# define STATUS_IMPLEMENTATION_LIMIT ((NTSTATUS) 0xC000042BL)
+#endif
+
+#ifndef STATUS_ELEVATION_REQUIRED
+# define STATUS_ELEVATION_REQUIRED ((NTSTATUS) 0xC000042CL)
+#endif
+
+#ifndef STATUS_NO_SECURITY_CONTEXT
+# define STATUS_NO_SECURITY_CONTEXT ((NTSTATUS) 0xC000042DL)
+#endif
+
+#ifndef STATUS_PKU2U_CERT_FAILURE
+# define STATUS_PKU2U_CERT_FAILURE ((NTSTATUS) 0xC000042FL)
+#endif
+
+#ifndef STATUS_BEYOND_VDL
+# define STATUS_BEYOND_VDL ((NTSTATUS) 0xC0000432L)
+#endif
+
+#ifndef STATUS_ENCOUNTERED_WRITE_IN_PROGRESS
+# define STATUS_ENCOUNTERED_WRITE_IN_PROGRESS ((NTSTATUS) 0xC0000433L)
+#endif
+
+#ifndef STATUS_PTE_CHANGED
+# define STATUS_PTE_CHANGED ((NTSTATUS) 0xC0000434L)
+#endif
+
+#ifndef STATUS_PURGE_FAILED
+# define STATUS_PURGE_FAILED ((NTSTATUS) 0xC0000435L)
+#endif
+
+#ifndef STATUS_CRED_REQUIRES_CONFIRMATION
+# define STATUS_CRED_REQUIRES_CONFIRMATION ((NTSTATUS) 0xC0000440L)
+#endif
+
+#ifndef STATUS_CS_ENCRYPTION_INVALID_SERVER_RESPONSE
+# define STATUS_CS_ENCRYPTION_INVALID_SERVER_RESPONSE ((NTSTATUS) 0xC0000441L)
+#endif
+
+#ifndef STATUS_CS_ENCRYPTION_UNSUPPORTED_SERVER
+# define STATUS_CS_ENCRYPTION_UNSUPPORTED_SERVER ((NTSTATUS) 0xC0000442L)
+#endif
+
+#ifndef STATUS_CS_ENCRYPTION_EXISTING_ENCRYPTED_FILE
+# define STATUS_CS_ENCRYPTION_EXISTING_ENCRYPTED_FILE ((NTSTATUS) 0xC0000443L)
+#endif
+
+#ifndef STATUS_CS_ENCRYPTION_NEW_ENCRYPTED_FILE
+# define STATUS_CS_ENCRYPTION_NEW_ENCRYPTED_FILE ((NTSTATUS) 0xC0000444L)
+#endif
+
+#ifndef STATUS_CS_ENCRYPTION_FILE_NOT_CSE
+# define STATUS_CS_ENCRYPTION_FILE_NOT_CSE ((NTSTATUS) 0xC0000445L)
+#endif
+
+#ifndef STATUS_INVALID_LABEL
+# define STATUS_INVALID_LABEL ((NTSTATUS) 0xC0000446L)
+#endif
+
+#ifndef STATUS_DRIVER_PROCESS_TERMINATED
+# define STATUS_DRIVER_PROCESS_TERMINATED ((NTSTATUS) 0xC0000450L)
+#endif
+
+#ifndef STATUS_AMBIGUOUS_SYSTEM_DEVICE
+# define STATUS_AMBIGUOUS_SYSTEM_DEVICE ((NTSTATUS) 0xC0000451L)
+#endif
+
+#ifndef STATUS_SYSTEM_DEVICE_NOT_FOUND
+# define STATUS_SYSTEM_DEVICE_NOT_FOUND ((NTSTATUS) 0xC0000452L)
+#endif
+
+#ifndef STATUS_RESTART_BOOT_APPLICATION
+# define STATUS_RESTART_BOOT_APPLICATION ((NTSTATUS) 0xC0000453L)
+#endif
+
+#ifndef STATUS_INSUFFICIENT_NVRAM_RESOURCES
+# define STATUS_INSUFFICIENT_NVRAM_RESOURCES ((NTSTATUS) 0xC0000454L)
+#endif
+
+#ifndef STATUS_INVALID_TASK_NAME
+# define STATUS_INVALID_TASK_NAME ((NTSTATUS) 0xC0000500L)
+#endif
+
+#ifndef STATUS_INVALID_TASK_INDEX
+# define STATUS_INVALID_TASK_INDEX ((NTSTATUS) 0xC0000501L)
+#endif
+
+#ifndef STATUS_THREAD_ALREADY_IN_TASK
+# define STATUS_THREAD_ALREADY_IN_TASK ((NTSTATUS) 0xC0000502L)
+#endif
+
+#ifndef STATUS_CALLBACK_BYPASS
+# define STATUS_CALLBACK_BYPASS ((NTSTATUS) 0xC0000503L)
+#endif
+
+#ifndef STATUS_FAIL_FAST_EXCEPTION
+# define STATUS_FAIL_FAST_EXCEPTION ((NTSTATUS) 0xC0000602L)
+#endif
+
+#ifndef STATUS_IMAGE_CERT_REVOKED
+# define STATUS_IMAGE_CERT_REVOKED ((NTSTATUS) 0xC0000603L)
+#endif
+
+#ifndef STATUS_PORT_CLOSED
+# define STATUS_PORT_CLOSED ((NTSTATUS) 0xC0000700L)
+#endif
+
+#ifndef STATUS_MESSAGE_LOST
+# define STATUS_MESSAGE_LOST ((NTSTATUS) 0xC0000701L)
+#endif
+
+#ifndef STATUS_INVALID_MESSAGE
+# define STATUS_INVALID_MESSAGE ((NTSTATUS) 0xC0000702L)
+#endif
+
+#ifndef STATUS_REQUEST_CANCELED
+# define STATUS_REQUEST_CANCELED ((NTSTATUS) 0xC0000703L)
+#endif
+
+#ifndef STATUS_RECURSIVE_DISPATCH
+# define STATUS_RECURSIVE_DISPATCH ((NTSTATUS) 0xC0000704L)
+#endif
+
+#ifndef STATUS_LPC_RECEIVE_BUFFER_EXPECTED
+# define STATUS_LPC_RECEIVE_BUFFER_EXPECTED ((NTSTATUS) 0xC0000705L)
+#endif
+
+#ifndef STATUS_LPC_INVALID_CONNECTION_USAGE
+# define STATUS_LPC_INVALID_CONNECTION_USAGE ((NTSTATUS) 0xC0000706L)
+#endif
+
+#ifndef STATUS_LPC_REQUESTS_NOT_ALLOWED
+# define STATUS_LPC_REQUESTS_NOT_ALLOWED ((NTSTATUS) 0xC0000707L)
+#endif
+
+#ifndef STATUS_RESOURCE_IN_USE
+# define STATUS_RESOURCE_IN_USE ((NTSTATUS) 0xC0000708L)
+#endif
+
+#ifndef STATUS_HARDWARE_MEMORY_ERROR
+# define STATUS_HARDWARE_MEMORY_ERROR ((NTSTATUS) 0xC0000709L)
+#endif
+
+#ifndef STATUS_THREADPOOL_HANDLE_EXCEPTION
+# define STATUS_THREADPOOL_HANDLE_EXCEPTION ((NTSTATUS) 0xC000070AL)
+#endif
+
+#ifndef STATUS_THREADPOOL_SET_EVENT_ON_COMPLETION_FAILED
+# define STATUS_THREADPOOL_SET_EVENT_ON_COMPLETION_FAILED ((NTSTATUS) 0xC000070BL)
+#endif
+
+#ifndef STATUS_THREADPOOL_RELEASE_SEMAPHORE_ON_COMPLETION_FAILED
+# define STATUS_THREADPOOL_RELEASE_SEMAPHORE_ON_COMPLETION_FAILED ((NTSTATUS) 0xC000070CL)
+#endif
+
+#ifndef STATUS_THREADPOOL_RELEASE_MUTEX_ON_COMPLETION_FAILED
+# define STATUS_THREADPOOL_RELEASE_MUTEX_ON_COMPLETION_FAILED ((NTSTATUS) 0xC000070DL)
+#endif
+
+#ifndef STATUS_THREADPOOL_FREE_LIBRARY_ON_COMPLETION_FAILED
+# define STATUS_THREADPOOL_FREE_LIBRARY_ON_COMPLETION_FAILED ((NTSTATUS) 0xC000070EL)
+#endif
+
+#ifndef STATUS_THREADPOOL_RELEASED_DURING_OPERATION
+# define STATUS_THREADPOOL_RELEASED_DURING_OPERATION ((NTSTATUS) 0xC000070FL)
+#endif
+
+#ifndef STATUS_CALLBACK_RETURNED_WHILE_IMPERSONATING
+# define STATUS_CALLBACK_RETURNED_WHILE_IMPERSONATING ((NTSTATUS) 0xC0000710L)
+#endif
+
+#ifndef STATUS_APC_RETURNED_WHILE_IMPERSONATING
+# define STATUS_APC_RETURNED_WHILE_IMPERSONATING ((NTSTATUS) 0xC0000711L)
+#endif
+
+#ifndef STATUS_PROCESS_IS_PROTECTED
+# define STATUS_PROCESS_IS_PROTECTED ((NTSTATUS) 0xC0000712L)
+#endif
+
+#ifndef STATUS_MCA_EXCEPTION
+# define STATUS_MCA_EXCEPTION ((NTSTATUS) 0xC0000713L)
+#endif
+
+#ifndef STATUS_CERTIFICATE_MAPPING_NOT_UNIQUE
+# define STATUS_CERTIFICATE_MAPPING_NOT_UNIQUE ((NTSTATUS) 0xC0000714L)
+#endif
+
+#ifndef STATUS_SYMLINK_CLASS_DISABLED
+# define STATUS_SYMLINK_CLASS_DISABLED ((NTSTATUS) 0xC0000715L)
+#endif
+
+#ifndef STATUS_INVALID_IDN_NORMALIZATION
+# define STATUS_INVALID_IDN_NORMALIZATION ((NTSTATUS) 0xC0000716L)
+#endif
+
+#ifndef STATUS_NO_UNICODE_TRANSLATION
+# define STATUS_NO_UNICODE_TRANSLATION ((NTSTATUS) 0xC0000717L)
+#endif
+
+#ifndef STATUS_ALREADY_REGISTERED
+# define STATUS_ALREADY_REGISTERED ((NTSTATUS) 0xC0000718L)
+#endif
+
+#ifndef STATUS_CONTEXT_MISMATCH
+# define STATUS_CONTEXT_MISMATCH ((NTSTATUS) 0xC0000719L)
+#endif
+
+#ifndef STATUS_PORT_ALREADY_HAS_COMPLETION_LIST
+# define STATUS_PORT_ALREADY_HAS_COMPLETION_LIST ((NTSTATUS) 0xC000071AL)
+#endif
+
+#ifndef STATUS_CALLBACK_RETURNED_THREAD_PRIORITY
+# define STATUS_CALLBACK_RETURNED_THREAD_PRIORITY ((NTSTATUS) 0xC000071BL)
+#endif
+
+#ifndef STATUS_INVALID_THREAD
+# define STATUS_INVALID_THREAD ((NTSTATUS) 0xC000071CL)
+#endif
+
+#ifndef STATUS_CALLBACK_RETURNED_TRANSACTION
+# define STATUS_CALLBACK_RETURNED_TRANSACTION ((NTSTATUS) 0xC000071DL)
+#endif
+
+#ifndef STATUS_CALLBACK_RETURNED_LDR_LOCK
+# define STATUS_CALLBACK_RETURNED_LDR_LOCK ((NTSTATUS) 0xC000071EL)
+#endif
+
+#ifndef STATUS_CALLBACK_RETURNED_LANG
+# define STATUS_CALLBACK_RETURNED_LANG ((NTSTATUS) 0xC000071FL)
+#endif
+
+#ifndef STATUS_CALLBACK_RETURNED_PRI_BACK
+# define STATUS_CALLBACK_RETURNED_PRI_BACK ((NTSTATUS) 0xC0000720L)
+#endif
+
+#ifndef STATUS_CALLBACK_RETURNED_THREAD_AFFINITY
+# define STATUS_CALLBACK_RETURNED_THREAD_AFFINITY ((NTSTATUS) 0xC0000721L)
+#endif
+
+#ifndef STATUS_DISK_REPAIR_DISABLED
+# define STATUS_DISK_REPAIR_DISABLED ((NTSTATUS) 0xC0000800L)
+#endif
+
+#ifndef STATUS_DS_DOMAIN_RENAME_IN_PROGRESS
+# define STATUS_DS_DOMAIN_RENAME_IN_PROGRESS ((NTSTATUS) 0xC0000801L)
+#endif
+
+#ifndef STATUS_DISK_QUOTA_EXCEEDED
+# define STATUS_DISK_QUOTA_EXCEEDED ((NTSTATUS) 0xC0000802L)
+#endif
+
+#ifndef STATUS_DATA_LOST_REPAIR
+# define STATUS_DATA_LOST_REPAIR ((NTSTATUS) 0x80000803L)
+#endif
+
+#ifndef STATUS_CONTENT_BLOCKED
+# define STATUS_CONTENT_BLOCKED ((NTSTATUS) 0xC0000804L)
+#endif
+
+#ifndef STATUS_BAD_CLUSTERS
+# define STATUS_BAD_CLUSTERS ((NTSTATUS) 0xC0000805L)
+#endif
+
+#ifndef STATUS_VOLUME_DIRTY
+# define STATUS_VOLUME_DIRTY ((NTSTATUS) 0xC0000806L)
+#endif
+
+#ifndef STATUS_FILE_CHECKED_OUT
+# define STATUS_FILE_CHECKED_OUT ((NTSTATUS) 0xC0000901L)
+#endif
+
+#ifndef STATUS_CHECKOUT_REQUIRED
+# define STATUS_CHECKOUT_REQUIRED ((NTSTATUS) 0xC0000902L)
+#endif
+
+#ifndef STATUS_BAD_FILE_TYPE
+# define STATUS_BAD_FILE_TYPE ((NTSTATUS) 0xC0000903L)
+#endif
+
+#ifndef STATUS_FILE_TOO_LARGE
+# define STATUS_FILE_TOO_LARGE ((NTSTATUS) 0xC0000904L)
+#endif
+
+#ifndef STATUS_FORMS_AUTH_REQUIRED
+# define STATUS_FORMS_AUTH_REQUIRED ((NTSTATUS) 0xC0000905L)
+#endif
+
+#ifndef STATUS_VIRUS_INFECTED
+# define STATUS_VIRUS_INFECTED ((NTSTATUS) 0xC0000906L)
+#endif
+
+#ifndef STATUS_VIRUS_DELETED
+# define STATUS_VIRUS_DELETED ((NTSTATUS) 0xC0000907L)
+#endif
+
+#ifndef STATUS_BAD_MCFG_TABLE
+# define STATUS_BAD_MCFG_TABLE ((NTSTATUS) 0xC0000908L)
+#endif
+
+#ifndef STATUS_CANNOT_BREAK_OPLOCK
+# define STATUS_CANNOT_BREAK_OPLOCK ((NTSTATUS) 0xC0000909L)
+#endif
+
+#ifndef STATUS_WOW_ASSERTION
+# define STATUS_WOW_ASSERTION ((NTSTATUS) 0xC0009898L)
+#endif
+
+#ifndef STATUS_INVALID_SIGNATURE
+# define STATUS_INVALID_SIGNATURE ((NTSTATUS) 0xC000A000L)
+#endif
+
+#ifndef STATUS_HMAC_NOT_SUPPORTED
+# define STATUS_HMAC_NOT_SUPPORTED ((NTSTATUS) 0xC000A001L)
+#endif
+
+#ifndef STATUS_AUTH_TAG_MISMATCH
+# define STATUS_AUTH_TAG_MISMATCH ((NTSTATUS) 0xC000A002L)
+#endif
+
+#ifndef STATUS_IPSEC_QUEUE_OVERFLOW
+# define STATUS_IPSEC_QUEUE_OVERFLOW ((NTSTATUS) 0xC000A010L)
+#endif
+
+#ifndef STATUS_ND_QUEUE_OVERFLOW
+# define STATUS_ND_QUEUE_OVERFLOW ((NTSTATUS) 0xC000A011L)
+#endif
+
+#ifndef STATUS_HOPLIMIT_EXCEEDED
+# define STATUS_HOPLIMIT_EXCEEDED ((NTSTATUS) 0xC000A012L)
+#endif
+
+#ifndef STATUS_PROTOCOL_NOT_SUPPORTED
+# define STATUS_PROTOCOL_NOT_SUPPORTED ((NTSTATUS) 0xC000A013L)
+#endif
+
+#ifndef STATUS_FASTPATH_REJECTED
+# define STATUS_FASTPATH_REJECTED ((NTSTATUS) 0xC000A014L)
+#endif
+
+#ifndef STATUS_LOST_WRITEBEHIND_DATA_NETWORK_DISCONNECTED
+# define STATUS_LOST_WRITEBEHIND_DATA_NETWORK_DISCONNECTED ((NTSTATUS) 0xC000A080L)
+#endif
+
+#ifndef STATUS_LOST_WRITEBEHIND_DATA_NETWORK_SERVER_ERROR
+# define STATUS_LOST_WRITEBEHIND_DATA_NETWORK_SERVER_ERROR ((NTSTATUS) 0xC000A081L)
+#endif
+
+#ifndef STATUS_LOST_WRITEBEHIND_DATA_LOCAL_DISK_ERROR
+# define STATUS_LOST_WRITEBEHIND_DATA_LOCAL_DISK_ERROR ((NTSTATUS) 0xC000A082L)
+#endif
+
+#ifndef STATUS_XML_PARSE_ERROR
+# define STATUS_XML_PARSE_ERROR ((NTSTATUS) 0xC000A083L)
+#endif
+
+#ifndef STATUS_XMLDSIG_ERROR
+# define STATUS_XMLDSIG_ERROR ((NTSTATUS) 0xC000A084L)
+#endif
+
+#ifndef STATUS_WRONG_COMPARTMENT
+# define STATUS_WRONG_COMPARTMENT ((NTSTATUS) 0xC000A085L)
+#endif
+
+#ifndef STATUS_AUTHIP_FAILURE
+# define STATUS_AUTHIP_FAILURE ((NTSTATUS) 0xC000A086L)
+#endif
+
+#ifndef STATUS_DS_OID_MAPPED_GROUP_CANT_HAVE_MEMBERS
+# define STATUS_DS_OID_MAPPED_GROUP_CANT_HAVE_MEMBERS ((NTSTATUS) 0xC000A087L)
+#endif
+
+#ifndef STATUS_DS_OID_NOT_FOUND
+# define STATUS_DS_OID_NOT_FOUND ((NTSTATUS) 0xC000A088L)
+#endif
+
+#ifndef STATUS_HASH_NOT_SUPPORTED
+# define STATUS_HASH_NOT_SUPPORTED ((NTSTATUS) 0xC000A100L)
+#endif
+
+#ifndef STATUS_HASH_NOT_PRESENT
+# define STATUS_HASH_NOT_PRESENT ((NTSTATUS) 0xC000A101L)
+#endif
+
+/* This is not the NTSTATUS_FROM_WIN32 that the DDK provides, because the DDK
+ * got it wrong! */
+#ifdef NTSTATUS_FROM_WIN32
+# undef NTSTATUS_FROM_WIN32
+#endif
+#define NTSTATUS_FROM_WIN32(error) ((NTSTATUS) (error) <= 0 ? \
+        ((NTSTATUS) (error)) : ((NTSTATUS) (((error) & 0x0000FFFF) | \
+        (FACILITY_NTWIN32 << 16) | ERROR_SEVERITY_WARNING)))
+
+#ifndef JOB_OBJECT_LIMIT_PROCESS_MEMORY
+# define JOB_OBJECT_LIMIT_PROCESS_MEMORY             0x00000100
+#endif
+#ifndef JOB_OBJECT_LIMIT_JOB_MEMORY
+# define JOB_OBJECT_LIMIT_JOB_MEMORY                 0x00000200
+#endif
+#ifndef JOB_OBJECT_LIMIT_DIE_ON_UNHANDLED_EXCEPTION
+# define JOB_OBJECT_LIMIT_DIE_ON_UNHANDLED_EXCEPTION 0x00000400
+#endif
+#ifndef JOB_OBJECT_LIMIT_BREAKAWAY_OK
+# define JOB_OBJECT_LIMIT_BREAKAWAY_OK               0x00000800
+#endif
+#ifndef JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK
+# define JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK        0x00001000
+#endif
+#ifndef JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE
+# define JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE          0x00002000
+#endif
+
+#ifndef SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE
+# define SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE 0x00000002
+#endif
+
+/* from winternl.h */
+#if !defined(__UNICODE_STRING_DEFINED) && defined(__MINGW32__)
+#define __UNICODE_STRING_DEFINED
+#endif
+typedef struct _UNICODE_STRING {
+  USHORT Length;
+  USHORT MaximumLength;
+  PWSTR  Buffer;
+} UNICODE_STRING, *PUNICODE_STRING;
+
+typedef const UNICODE_STRING *PCUNICODE_STRING;
+
+/* from ntifs.h */
+#ifndef DEVICE_TYPE
+# define DEVICE_TYPE DWORD
+#endif
+
+/* MinGW already has a definition for REPARSE_DATA_BUFFER, but mingw-w64 does
+ * not.
+ */
+#if defined(_MSC_VER) || defined(__MINGW64_VERSION_MAJOR)
+  typedef struct _REPARSE_DATA_BUFFER {
+    ULONG  ReparseTag;
+    USHORT ReparseDataLength;
+    USHORT Reserved;
+    union {
+      struct {
+        USHORT SubstituteNameOffset;
+        USHORT SubstituteNameLength;
+        USHORT PrintNameOffset;
+        USHORT PrintNameLength;
+        ULONG Flags;
+        WCHAR PathBuffer[1];
+      } SymbolicLinkReparseBuffer;
+      struct {
+        USHORT SubstituteNameOffset;
+        USHORT SubstituteNameLength;
+        USHORT PrintNameOffset;
+        USHORT PrintNameLength;
+        WCHAR PathBuffer[1];
+      } MountPointReparseBuffer;
+      struct {
+        UCHAR  DataBuffer[1];
+      } GenericReparseBuffer;
+      struct {
+        ULONG StringCount;
+        WCHAR StringList[1];
+      } AppExecLinkReparseBuffer;
+    };
+  } REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER;
+#endif
+
+typedef struct _IO_STATUS_BLOCK {
+  union {
+    NTSTATUS Status;
+    PVOID Pointer;
+  };
+  ULONG_PTR Information;
+} IO_STATUS_BLOCK, *PIO_STATUS_BLOCK;
+
+typedef enum _FILE_INFORMATION_CLASS {
+  FileDirectoryInformation = 1,
+  FileFullDirectoryInformation,
+  FileBothDirectoryInformation,
+  FileBasicInformation,
+  FileStandardInformation,
+  FileInternalInformation,
+  FileEaInformation,
+  FileAccessInformation,
+  FileNameInformation,
+  FileRenameInformation,
+  FileLinkInformation,
+  FileNamesInformation,
+  FileDispositionInformation,
+  FilePositionInformation,
+  FileFullEaInformation,
+  FileModeInformation,
+  FileAlignmentInformation,
+  FileAllInformation,
+  FileAllocationInformation,
+  FileEndOfFileInformation,
+  FileAlternateNameInformation,
+  FileStreamInformation,
+  FilePipeInformation,
+  FilePipeLocalInformation,
+  FilePipeRemoteInformation,
+  FileMailslotQueryInformation,
+  FileMailslotSetInformation,
+  FileCompressionInformation,
+  FileObjectIdInformation,
+  FileCompletionInformation,
+  FileMoveClusterInformation,
+  FileQuotaInformation,
+  FileReparsePointInformation,
+  FileNetworkOpenInformation,
+  FileAttributeTagInformation,
+  FileTrackingInformation,
+  FileIdBothDirectoryInformation,
+  FileIdFullDirectoryInformation,
+  FileValidDataLengthInformation,
+  FileShortNameInformation,
+  FileIoCompletionNotificationInformation,
+  FileIoStatusBlockRangeInformation,
+  FileIoPriorityHintInformation,
+  FileSfioReserveInformation,
+  FileSfioVolumeInformation,
+  FileHardLinkInformation,
+  FileProcessIdsUsingFileInformation,
+  FileNormalizedNameInformation,
+  FileNetworkPhysicalNameInformation,
+  FileIdGlobalTxDirectoryInformation,
+  FileIsRemoteDeviceInformation,
+  FileAttributeCacheInformation,
+  FileNumaNodeInformation,
+  FileStandardLinkInformation,
+  FileRemoteProtocolInformation,
+  FileMaximumInformation
+} FILE_INFORMATION_CLASS, *PFILE_INFORMATION_CLASS;
+
+typedef struct _FILE_DIRECTORY_INFORMATION {
+  ULONG NextEntryOffset;
+  ULONG FileIndex;
+  LARGE_INTEGER CreationTime;
+  LARGE_INTEGER LastAccessTime;
+  LARGE_INTEGER LastWriteTime;
+  LARGE_INTEGER ChangeTime;
+  LARGE_INTEGER EndOfFile;
+  LARGE_INTEGER AllocationSize;
+  ULONG FileAttributes;
+  ULONG FileNameLength;
+  WCHAR FileName[1];
+} FILE_DIRECTORY_INFORMATION, *PFILE_DIRECTORY_INFORMATION;
+
+typedef struct _FILE_BOTH_DIR_INFORMATION {
+  ULONG NextEntryOffset;
+  ULONG FileIndex;
+  LARGE_INTEGER CreationTime;
+  LARGE_INTEGER LastAccessTime;
+  LARGE_INTEGER LastWriteTime;
+  LARGE_INTEGER ChangeTime;
+  LARGE_INTEGER EndOfFile;
+  LARGE_INTEGER AllocationSize;
+  ULONG FileAttributes;
+  ULONG FileNameLength;
+  ULONG EaSize;
+  CCHAR ShortNameLength;
+  WCHAR ShortName[12];
+  WCHAR FileName[1];
+} FILE_BOTH_DIR_INFORMATION, *PFILE_BOTH_DIR_INFORMATION;
+
+typedef struct _FILE_BASIC_INFORMATION {
+  LARGE_INTEGER CreationTime;
+  LARGE_INTEGER LastAccessTime;
+  LARGE_INTEGER LastWriteTime;
+  LARGE_INTEGER ChangeTime;
+  DWORD FileAttributes;
+} FILE_BASIC_INFORMATION, *PFILE_BASIC_INFORMATION;
+
+typedef struct _FILE_STANDARD_INFORMATION {
+  LARGE_INTEGER AllocationSize;
+  LARGE_INTEGER EndOfFile;
+  ULONG         NumberOfLinks;
+  BOOLEAN       DeletePending;
+  BOOLEAN       Directory;
+} FILE_STANDARD_INFORMATION, *PFILE_STANDARD_INFORMATION;
+
+typedef struct _FILE_INTERNAL_INFORMATION {
+  LARGE_INTEGER IndexNumber;
+} FILE_INTERNAL_INFORMATION, *PFILE_INTERNAL_INFORMATION;
+
+typedef struct _FILE_EA_INFORMATION {
+  ULONG EaSize;
+} FILE_EA_INFORMATION, *PFILE_EA_INFORMATION;
+
+typedef struct _FILE_ACCESS_INFORMATION {
+  ACCESS_MASK AccessFlags;
+} FILE_ACCESS_INFORMATION, *PFILE_ACCESS_INFORMATION;
+
+typedef struct _FILE_POSITION_INFORMATION {
+  LARGE_INTEGER CurrentByteOffset;
+} FILE_POSITION_INFORMATION, *PFILE_POSITION_INFORMATION;
+
+typedef struct _FILE_MODE_INFORMATION {
+  ULONG Mode;
+} FILE_MODE_INFORMATION, *PFILE_MODE_INFORMATION;
+
+typedef struct _FILE_ALIGNMENT_INFORMATION {
+  ULONG AlignmentRequirement;
+} FILE_ALIGNMENT_INFORMATION, *PFILE_ALIGNMENT_INFORMATION;
+
+typedef struct _FILE_NAME_INFORMATION {
+  ULONG FileNameLength;
+  WCHAR FileName[1];
+} FILE_NAME_INFORMATION, *PFILE_NAME_INFORMATION;
+
+typedef struct _FILE_END_OF_FILE_INFORMATION {
+  LARGE_INTEGER  EndOfFile;
+} FILE_END_OF_FILE_INFORMATION, *PFILE_END_OF_FILE_INFORMATION;
+
+typedef struct _FILE_ALL_INFORMATION {
+  FILE_BASIC_INFORMATION     BasicInformation;
+  FILE_STANDARD_INFORMATION  StandardInformation;
+  FILE_INTERNAL_INFORMATION  InternalInformation;
+  FILE_EA_INFORMATION        EaInformation;
+  FILE_ACCESS_INFORMATION    AccessInformation;
+  FILE_POSITION_INFORMATION  PositionInformation;
+  FILE_MODE_INFORMATION      ModeInformation;
+  FILE_ALIGNMENT_INFORMATION AlignmentInformation;
+  FILE_NAME_INFORMATION      NameInformation;
+} FILE_ALL_INFORMATION, *PFILE_ALL_INFORMATION;
+
+typedef struct _FILE_DISPOSITION_INFORMATION {
+  BOOLEAN DeleteFile;
+} FILE_DISPOSITION_INFORMATION, *PFILE_DISPOSITION_INFORMATION;
+
+typedef struct _FILE_PIPE_LOCAL_INFORMATION {
+  ULONG NamedPipeType;
+  ULONG NamedPipeConfiguration;
+  ULONG MaximumInstances;
+  ULONG CurrentInstances;
+  ULONG InboundQuota;
+  ULONG ReadDataAvailable;
+  ULONG OutboundQuota;
+  ULONG WriteQuotaAvailable;
+  ULONG NamedPipeState;
+  ULONG NamedPipeEnd;
+} FILE_PIPE_LOCAL_INFORMATION, *PFILE_PIPE_LOCAL_INFORMATION;
+
+#define FILE_SYNCHRONOUS_IO_ALERT               0x00000010
+#define FILE_SYNCHRONOUS_IO_NONALERT            0x00000020
+
+typedef enum _FS_INFORMATION_CLASS {
+  FileFsVolumeInformation       = 1,
+  FileFsLabelInformation        = 2,
+  FileFsSizeInformation         = 3,
+  FileFsDeviceInformation       = 4,
+  FileFsAttributeInformation    = 5,
+  FileFsControlInformation      = 6,
+  FileFsFullSizeInformation     = 7,
+  FileFsObjectIdInformation     = 8,
+  FileFsDriverPathInformation   = 9,
+  FileFsVolumeFlagsInformation  = 10,
+  FileFsSectorSizeInformation   = 11
+} FS_INFORMATION_CLASS, *PFS_INFORMATION_CLASS;
+
+typedef struct _FILE_FS_VOLUME_INFORMATION {
+  LARGE_INTEGER VolumeCreationTime;
+  ULONG         VolumeSerialNumber;
+  ULONG         VolumeLabelLength;
+  BOOLEAN       SupportsObjects;
+  WCHAR         VolumeLabel[1];
+} FILE_FS_VOLUME_INFORMATION, *PFILE_FS_VOLUME_INFORMATION;
+
+typedef struct _FILE_FS_LABEL_INFORMATION {
+  ULONG VolumeLabelLength;
+  WCHAR VolumeLabel[1];
+} FILE_FS_LABEL_INFORMATION, *PFILE_FS_LABEL_INFORMATION;
+
+typedef struct _FILE_FS_SIZE_INFORMATION {
+  LARGE_INTEGER TotalAllocationUnits;
+  LARGE_INTEGER AvailableAllocationUnits;
+  ULONG         SectorsPerAllocationUnit;
+  ULONG         BytesPerSector;
+} FILE_FS_SIZE_INFORMATION, *PFILE_FS_SIZE_INFORMATION;
+
+typedef struct _FILE_FS_DEVICE_INFORMATION {
+  DEVICE_TYPE DeviceType;
+  ULONG       Characteristics;
+} FILE_FS_DEVICE_INFORMATION, *PFILE_FS_DEVICE_INFORMATION;
+
+typedef struct _FILE_FS_ATTRIBUTE_INFORMATION {
+  ULONG FileSystemAttributes;
+  LONG  MaximumComponentNameLength;
+  ULONG FileSystemNameLength;
+  WCHAR FileSystemName[1];
+} FILE_FS_ATTRIBUTE_INFORMATION, *PFILE_FS_ATTRIBUTE_INFORMATION;
+
+typedef struct _FILE_FS_CONTROL_INFORMATION {
+  LARGE_INTEGER FreeSpaceStartFiltering;
+  LARGE_INTEGER FreeSpaceThreshold;
+  LARGE_INTEGER FreeSpaceStopFiltering;
+  LARGE_INTEGER DefaultQuotaThreshold;
+  LARGE_INTEGER DefaultQuotaLimit;
+  ULONG         FileSystemControlFlags;
+} FILE_FS_CONTROL_INFORMATION, *PFILE_FS_CONTROL_INFORMATION;
+
+typedef struct _FILE_FS_FULL_SIZE_INFORMATION {
+  LARGE_INTEGER TotalAllocationUnits;
+  LARGE_INTEGER CallerAvailableAllocationUnits;
+  LARGE_INTEGER ActualAvailableAllocationUnits;
+  ULONG         SectorsPerAllocationUnit;
+  ULONG         BytesPerSector;
+} FILE_FS_FULL_SIZE_INFORMATION, *PFILE_FS_FULL_SIZE_INFORMATION;
+
+typedef struct _FILE_FS_OBJECTID_INFORMATION {
+  UCHAR ObjectId[16];
+  UCHAR ExtendedInfo[48];
+} FILE_FS_OBJECTID_INFORMATION, *PFILE_FS_OBJECTID_INFORMATION;
+
+typedef struct _FILE_FS_DRIVER_PATH_INFORMATION {
+  BOOLEAN DriverInPath;
+  ULONG   DriverNameLength;
+  WCHAR   DriverName[1];
+} FILE_FS_DRIVER_PATH_INFORMATION, *PFILE_FS_DRIVER_PATH_INFORMATION;
+
+typedef struct _FILE_FS_VOLUME_FLAGS_INFORMATION {
+  ULONG Flags;
+} FILE_FS_VOLUME_FLAGS_INFORMATION, *PFILE_FS_VOLUME_FLAGS_INFORMATION;
+
+typedef struct _FILE_FS_SECTOR_SIZE_INFORMATION {
+  ULONG LogicalBytesPerSector;
+  ULONG PhysicalBytesPerSectorForAtomicity;
+  ULONG PhysicalBytesPerSectorForPerformance;
+  ULONG FileSystemEffectivePhysicalBytesPerSectorForAtomicity;
+  ULONG Flags;
+  ULONG ByteOffsetForSectorAlignment;
+  ULONG ByteOffsetForPartitionAlignment;
+} FILE_FS_SECTOR_SIZE_INFORMATION, *PFILE_FS_SECTOR_SIZE_INFORMATION;
+
+typedef struct _SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION {
+    LARGE_INTEGER IdleTime;
+    LARGE_INTEGER KernelTime;
+    LARGE_INTEGER UserTime;
+    LARGE_INTEGER DpcTime;
+    LARGE_INTEGER InterruptTime;
+    ULONG InterruptCount;
+} SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION, *PSYSTEM_PROCESSOR_PERFORMANCE_INFORMATION;
+
+#ifndef SystemProcessorPerformanceInformation
+# define SystemProcessorPerformanceInformation 8
+#endif
+
+#ifndef ProcessConsoleHostProcess
+# define ProcessConsoleHostProcess 49
+#endif
+
+#ifndef FILE_DEVICE_FILE_SYSTEM
+# define FILE_DEVICE_FILE_SYSTEM 0x00000009
+#endif
+
+#ifndef FILE_DEVICE_NETWORK
+# define FILE_DEVICE_NETWORK 0x00000012
+#endif
+
+#ifndef METHOD_BUFFERED
+# define METHOD_BUFFERED 0
+#endif
+
+#ifndef METHOD_IN_DIRECT
+# define METHOD_IN_DIRECT 1
+#endif
+
+#ifndef METHOD_OUT_DIRECT
+# define METHOD_OUT_DIRECT 2
+#endif
+
+#ifndef METHOD_NEITHER
+#define METHOD_NEITHER 3
+#endif
+
+#ifndef METHOD_DIRECT_TO_HARDWARE
+# define METHOD_DIRECT_TO_HARDWARE METHOD_IN_DIRECT
+#endif
+
+#ifndef METHOD_DIRECT_FROM_HARDWARE
+# define METHOD_DIRECT_FROM_HARDWARE METHOD_OUT_DIRECT
+#endif
+
+#ifndef FILE_ANY_ACCESS
+# define FILE_ANY_ACCESS 0
+#endif
+
+#ifndef FILE_SPECIAL_ACCESS
+# define FILE_SPECIAL_ACCESS (FILE_ANY_ACCESS)
+#endif
+
+#ifndef FILE_READ_ACCESS
+# define FILE_READ_ACCESS 0x0001
+#endif
+
+#ifndef FILE_WRITE_ACCESS
+# define FILE_WRITE_ACCESS 0x0002
+#endif
+
+#ifndef CTL_CODE
+# define CTL_CODE(device_type, function, method, access)                      \
+    (((device_type) << 16) | ((access) << 14) | ((function) << 2) | (method))
+#endif
+
+#ifndef FSCTL_SET_REPARSE_POINT
+# define FSCTL_SET_REPARSE_POINT CTL_CODE(FILE_DEVICE_FILE_SYSTEM,            \
+                                          41,                                 \
+                                          METHOD_BUFFERED,                    \
+                                          FILE_SPECIAL_ACCESS)
+#endif
+
+#ifndef FSCTL_GET_REPARSE_POINT
+# define FSCTL_GET_REPARSE_POINT CTL_CODE(FILE_DEVICE_FILE_SYSTEM,            \
+                                          42,                                 \
+                                          METHOD_BUFFERED,                    \
+                                          FILE_ANY_ACCESS)
+#endif
+
+#ifndef FSCTL_DELETE_REPARSE_POINT
+# define FSCTL_DELETE_REPARSE_POINT CTL_CODE(FILE_DEVICE_FILE_SYSTEM,         \
+                                             43,                              \
+                                             METHOD_BUFFERED,                 \
+                                             FILE_SPECIAL_ACCESS)
+#endif
+
+#ifndef IO_REPARSE_TAG_SYMLINK
+# define IO_REPARSE_TAG_SYMLINK (0xA000000CL)
+#endif
+#ifndef IO_REPARSE_TAG_APPEXECLINK
+# define IO_REPARSE_TAG_APPEXECLINK (0x8000001BL)
+#endif
+
+typedef VOID (NTAPI *PIO_APC_ROUTINE)
+             (PVOID ApcContext,
+              PIO_STATUS_BLOCK IoStatusBlock,
+              ULONG Reserved);
+
+typedef NTSTATUS (NTAPI *sRtlGetVersion)
+                 (PRTL_OSVERSIONINFOW lpVersionInformation);
+
+typedef ULONG (NTAPI *sRtlNtStatusToDosError)
+              (NTSTATUS Status);
+
+typedef NTSTATUS (NTAPI *sNtDeviceIoControlFile)
+                 (HANDLE FileHandle,
+                  HANDLE Event,
+                  PIO_APC_ROUTINE ApcRoutine,
+                  PVOID ApcContext,
+                  PIO_STATUS_BLOCK IoStatusBlock,
+                  ULONG IoControlCode,
+                  PVOID InputBuffer,
+                  ULONG InputBufferLength,
+                  PVOID OutputBuffer,
+                  ULONG OutputBufferLength);
+
+typedef NTSTATUS (NTAPI *sNtQueryInformationFile)
+                 (HANDLE FileHandle,
+                  PIO_STATUS_BLOCK IoStatusBlock,
+                  PVOID FileInformation,
+                  ULONG Length,
+                  FILE_INFORMATION_CLASS FileInformationClass);
+
+typedef NTSTATUS (NTAPI *sNtSetInformationFile)
+                 (HANDLE FileHandle,
+                  PIO_STATUS_BLOCK IoStatusBlock,
+                  PVOID FileInformation,
+                  ULONG Length,
+                  FILE_INFORMATION_CLASS FileInformationClass);
+
+typedef NTSTATUS (NTAPI *sNtQueryVolumeInformationFile)
+                 (HANDLE FileHandle,
+                  PIO_STATUS_BLOCK IoStatusBlock,
+                  PVOID FsInformation,
+                  ULONG Length,
+                  FS_INFORMATION_CLASS FsInformationClass);
+
+typedef NTSTATUS (NTAPI *sNtQuerySystemInformation)
+                 (UINT SystemInformationClass,
+                  PVOID SystemInformation,
+                  ULONG SystemInformationLength,
+                  PULONG ReturnLength);
+
+typedef NTSTATUS (NTAPI *sNtQueryDirectoryFile)
+                 (HANDLE FileHandle,
+                  HANDLE Event,
+                  PIO_APC_ROUTINE ApcRoutine,
+                  PVOID ApcContext,
+                  PIO_STATUS_BLOCK IoStatusBlock,
+                  PVOID FileInformation,
+                  ULONG Length,
+                  FILE_INFORMATION_CLASS FileInformationClass,
+                  BOOLEAN ReturnSingleEntry,
+                  PUNICODE_STRING FileName,
+                  BOOLEAN RestartScan
+                );
+
+typedef NTSTATUS (NTAPI *sNtQueryInformationProcess)
+                 (HANDLE ProcessHandle,
+                  UINT ProcessInformationClass,
+                  PVOID ProcessInformation,
+                  ULONG Length,
+                  PULONG ReturnLength);
+
+/*
+ * Kernel32 headers
+ */
+#ifndef FILE_SKIP_COMPLETION_PORT_ON_SUCCESS
+# define FILE_SKIP_COMPLETION_PORT_ON_SUCCESS 0x1
+#endif
+
+#ifndef FILE_SKIP_SET_EVENT_ON_HANDLE
+# define FILE_SKIP_SET_EVENT_ON_HANDLE 0x2
+#endif
+
+#ifndef SYMBOLIC_LINK_FLAG_DIRECTORY
+# define SYMBOLIC_LINK_FLAG_DIRECTORY 0x1
+#endif
+
+#if defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR)
+  typedef struct _OVERLAPPED_ENTRY {
+      ULONG_PTR lpCompletionKey;
+      LPOVERLAPPED lpOverlapped;
+      ULONG_PTR Internal;
+      DWORD dwNumberOfBytesTransferred;
+  } OVERLAPPED_ENTRY, *LPOVERLAPPED_ENTRY;
+#endif
+
+/* from wincon.h */
+#ifndef ENABLE_INSERT_MODE
+# define ENABLE_INSERT_MODE 0x20
+#endif
+
+#ifndef ENABLE_QUICK_EDIT_MODE
+# define ENABLE_QUICK_EDIT_MODE 0x40
+#endif
+
+#ifndef ENABLE_EXTENDED_FLAGS
+# define ENABLE_EXTENDED_FLAGS 0x80
+#endif
+
+/* from winerror.h */
+#ifndef ERROR_ELEVATION_REQUIRED
+# define ERROR_ELEVATION_REQUIRED 740
+#endif
+
+#ifndef ERROR_SYMLINK_NOT_SUPPORTED
+# define ERROR_SYMLINK_NOT_SUPPORTED 1464
+#endif
+
+#ifndef ERROR_MUI_FILE_NOT_FOUND
+# define ERROR_MUI_FILE_NOT_FOUND 15100
+#endif
+
+#ifndef ERROR_MUI_INVALID_FILE
+# define ERROR_MUI_INVALID_FILE 15101
+#endif
+
+#ifndef ERROR_MUI_INVALID_RC_CONFIG
+# define ERROR_MUI_INVALID_RC_CONFIG 15102
+#endif
+
+#ifndef ERROR_MUI_INVALID_LOCALE_NAME
+# define ERROR_MUI_INVALID_LOCALE_NAME 15103
+#endif
+
+#ifndef ERROR_MUI_INVALID_ULTIMATEFALLBACK_NAME
+# define ERROR_MUI_INVALID_ULTIMATEFALLBACK_NAME 15104
+#endif
+
+#ifndef ERROR_MUI_FILE_NOT_LOADED
+# define ERROR_MUI_FILE_NOT_LOADED 15105
+#endif
+
+typedef BOOL (WINAPI *sGetQueuedCompletionStatusEx)
+             (HANDLE CompletionPort,
+              LPOVERLAPPED_ENTRY lpCompletionPortEntries,
+              ULONG ulCount,
+              PULONG ulNumEntriesRemoved,
+              DWORD dwMilliseconds,
+              BOOL fAlertable);
+
+/* from powerbase.h */
+#ifndef DEVICE_NOTIFY_CALLBACK
+# define DEVICE_NOTIFY_CALLBACK 2
+#endif
+
+#ifndef PBT_APMRESUMEAUTOMATIC
+# define PBT_APMRESUMEAUTOMATIC 18
+#endif
+
+#ifndef PBT_APMRESUMESUSPEND
+# define PBT_APMRESUMESUSPEND 7
+#endif
+
+typedef ULONG CALLBACK _DEVICE_NOTIFY_CALLBACK_ROUTINE(
+  PVOID Context,
+  ULONG Type,
+  PVOID Setting
+);
+typedef _DEVICE_NOTIFY_CALLBACK_ROUTINE* _PDEVICE_NOTIFY_CALLBACK_ROUTINE;
+
+typedef struct _DEVICE_NOTIFY_SUBSCRIBE_PARAMETERS {
+  _PDEVICE_NOTIFY_CALLBACK_ROUTINE Callback;
+  PVOID Context;
+} _DEVICE_NOTIFY_SUBSCRIBE_PARAMETERS, *_PDEVICE_NOTIFY_SUBSCRIBE_PARAMETERS;
+
+typedef PVOID _HPOWERNOTIFY;
+typedef _HPOWERNOTIFY *_PHPOWERNOTIFY;
+
+typedef DWORD (WINAPI *sPowerRegisterSuspendResumeNotification)
+              (DWORD         Flags,
+               HANDLE        Recipient,
+               _PHPOWERNOTIFY RegistrationHandle);
+
+/* from Winuser.h */
+typedef VOID (CALLBACK* WINEVENTPROC)
+             (HWINEVENTHOOK hWinEventHook,
+              DWORD         event,
+              HWND          hwnd,
+              LONG          idObject,
+              LONG          idChild,
+              DWORD         idEventThread,
+              DWORD         dwmsEventTime);
+
+typedef HWINEVENTHOOK (WINAPI *sSetWinEventHook)
+                      (UINT         eventMin,
+                       UINT         eventMax,
+                       HMODULE      hmodWinEventProc,
+                       WINEVENTPROC lpfnWinEventProc,
+                       DWORD        idProcess,
+                       DWORD        idThread,
+                       UINT         dwflags);
+
+/* From mstcpip.h */
+typedef struct _TCP_INITIAL_RTO_PARAMETERS {
+  USHORT Rtt;
+  UCHAR  MaxSynRetransmissions;
+} TCP_INITIAL_RTO_PARAMETERS, *PTCP_INITIAL_RTO_PARAMETERS;
+
+#ifndef TCP_INITIAL_RTO_NO_SYN_RETRANSMISSIONS
+# define TCP_INITIAL_RTO_NO_SYN_RETRANSMISSIONS ((UCHAR) -2)
+#endif
+#ifndef SIO_TCP_INITIAL_RTO
+# define  SIO_TCP_INITIAL_RTO _WSAIOW(IOC_VENDOR,17)
+#endif
+
+/* Ntdll function pointers */
+extern sRtlGetVersion pRtlGetVersion;
+extern sRtlNtStatusToDosError pRtlNtStatusToDosError;
+extern sNtDeviceIoControlFile pNtDeviceIoControlFile;
+extern sNtQueryInformationFile pNtQueryInformationFile;
+extern sNtSetInformationFile pNtSetInformationFile;
+extern sNtQueryVolumeInformationFile pNtQueryVolumeInformationFile;
+extern sNtQueryDirectoryFile pNtQueryDirectoryFile;
+extern sNtQuerySystemInformation pNtQuerySystemInformation;
+extern sNtQueryInformationProcess pNtQueryInformationProcess;
+
+/* Kernel32 function pointers */
+extern sGetQueuedCompletionStatusEx pGetQueuedCompletionStatusEx;
+
+/* Powrprof.dll function pointer */
+extern sPowerRegisterSuspendResumeNotification pPowerRegisterSuspendResumeNotification;
+
+/* User32.dll function pointer */
+extern sSetWinEventHook pSetWinEventHook;
+
+/* ws2_32.dll function pointer */
+/* mingw doesn't have this definition, so let's declare it here locally */
+typedef int (WINAPI *uv_sGetHostNameW)
+            (PWSTR,
+             int);
+extern uv_sGetHostNameW pGetHostNameW;
+
+#endif /* UV_WIN_WINAPI_H_ */
diff --git a/wpinet/src/main/native/thirdparty/libuv/src/win/winsock.cpp b/wpinet/src/main/native/thirdparty/libuv/src/win/winsock.cpp
new file mode 100644
index 0000000..cda82bc
--- /dev/null
+++ b/wpinet/src/main/native/thirdparty/libuv/src/win/winsock.cpp
@@ -0,0 +1,577 @@
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include <assert.h>
+#include <stdlib.h>
+
+#include "uv.h"
+#include "internal.h"
+
+
+#pragma comment(lib, "Ws2_32.lib")
+
+/* Whether there are any non-IFS LSPs stacked on TCP */
+int uv_tcp_non_ifs_lsp_ipv4;
+int uv_tcp_non_ifs_lsp_ipv6;
+
+/* Ip address used to bind to any port at any interface */
+struct sockaddr_in uv_addr_ip4_any_;
+struct sockaddr_in6 uv_addr_ip6_any_;
+
+
+/*
+ * Retrieves the pointer to a winsock extension function.
+ */
+static BOOL uv__get_extension_function(SOCKET socket, GUID guid,
+    void **target) {
+  int result;
+  DWORD bytes;
+
+  result = WSAIoctl(socket,
+                    SIO_GET_EXTENSION_FUNCTION_POINTER,
+                    &guid,
+                    sizeof(guid),
+                    (void*)target,
+                    sizeof(*target),
+                    &bytes,
+                    NULL,
+                    NULL);
+
+  if (result == SOCKET_ERROR) {
+    *target = NULL;
+    return FALSE;
+  } else {
+    return TRUE;
+  }
+}
+
+
+BOOL uv__get_acceptex_function(SOCKET socket, LPFN_ACCEPTEX* target) {
+  const GUID wsaid_acceptex = WSAID_ACCEPTEX;
+  return uv__get_extension_function(socket, wsaid_acceptex, (void**)target);
+}
+
+
+BOOL uv__get_connectex_function(SOCKET socket, LPFN_CONNECTEX* target) {
+  const GUID wsaid_connectex = WSAID_CONNECTEX;
+  return uv__get_extension_function(socket, wsaid_connectex, (void**)target);
+}
+
+
+
+void uv__winsock_init(void) {
+  WSADATA wsa_data;
+  int errorno;
+  SOCKET dummy;
+  WSAPROTOCOL_INFOW protocol_info;
+  int opt_len;
+
+  /* Set implicit binding address used by connectEx */
+  if (uv_ip4_addr("0.0.0.0", 0, &uv_addr_ip4_any_)) {
+    abort();
+  }
+
+  if (uv_ip6_addr("::", 0, &uv_addr_ip6_any_)) {
+    abort();
+  }
+
+  /* Skip initialization in safe mode without network support */
+  if (1 == GetSystemMetrics(SM_CLEANBOOT)) return;
+
+  /* Initialize winsock */
+  errorno = WSAStartup(MAKEWORD(2, 2), &wsa_data);
+  if (errorno != 0) {
+    uv_fatal_error(errorno, "WSAStartup");
+  }
+
+  /* Try to detect non-IFS LSPs */
+  uv_tcp_non_ifs_lsp_ipv4 = 1;
+  dummy = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
+  if (dummy != INVALID_SOCKET) {
+    opt_len = (int) sizeof protocol_info;
+    if (getsockopt(dummy,
+                   SOL_SOCKET,
+                   SO_PROTOCOL_INFOW,
+                   (char*) &protocol_info,
+                   &opt_len) == 0) {
+      if (protocol_info.dwServiceFlags1 & XP1_IFS_HANDLES)
+        uv_tcp_non_ifs_lsp_ipv4 = 0;
+    }
+    closesocket(dummy);
+  }
+
+  /* Try to detect IPV6 support and non-IFS LSPs */
+  uv_tcp_non_ifs_lsp_ipv6 = 1;
+  dummy = socket(AF_INET6, SOCK_STREAM, IPPROTO_IP);
+  if (dummy != INVALID_SOCKET) {
+    opt_len = (int) sizeof protocol_info;
+    if (getsockopt(dummy,
+                   SOL_SOCKET,
+                   SO_PROTOCOL_INFOW,
+                   (char*) &protocol_info,
+                   &opt_len) == 0) {
+      if (protocol_info.dwServiceFlags1 & XP1_IFS_HANDLES)
+        uv_tcp_non_ifs_lsp_ipv6 = 0;
+    }
+    closesocket(dummy);
+  }
+}
+
+
+int uv__ntstatus_to_winsock_error(NTSTATUS status) {
+  switch (status) {
+    case STATUS_SUCCESS:
+      return ERROR_SUCCESS;
+
+    case STATUS_PENDING:
+      return ERROR_IO_PENDING;
+
+    case STATUS_INVALID_HANDLE:
+    case STATUS_OBJECT_TYPE_MISMATCH:
+      return WSAENOTSOCK;
+
+    case STATUS_INSUFFICIENT_RESOURCES:
+    case STATUS_PAGEFILE_QUOTA:
+    case STATUS_COMMITMENT_LIMIT:
+    case STATUS_WORKING_SET_QUOTA:
+    case STATUS_NO_MEMORY:
+    case STATUS_QUOTA_EXCEEDED:
+    case STATUS_TOO_MANY_PAGING_FILES:
+    case STATUS_REMOTE_RESOURCES:
+      return WSAENOBUFS;
+
+    case STATUS_TOO_MANY_ADDRESSES:
+    case STATUS_SHARING_VIOLATION:
+    case STATUS_ADDRESS_ALREADY_EXISTS:
+      return WSAEADDRINUSE;
+
+    case STATUS_LINK_TIMEOUT:
+    case STATUS_IO_TIMEOUT:
+    case STATUS_TIMEOUT:
+      return WSAETIMEDOUT;
+
+    case STATUS_GRACEFUL_DISCONNECT:
+      return WSAEDISCON;
+
+    case STATUS_REMOTE_DISCONNECT:
+    case STATUS_CONNECTION_RESET:
+    case STATUS_LINK_FAILED:
+    case STATUS_CONNECTION_DISCONNECTED:
+    case STATUS_PORT_UNREACHABLE:
+    case STATUS_HOPLIMIT_EXCEEDED:
+      return WSAECONNRESET;
+
+    case STATUS_LOCAL_DISCONNECT:
+    case STATUS_TRANSACTION_ABORTED:
+    case STATUS_CONNECTION_ABORTED:
+      return WSAECONNABORTED;
+
+    case STATUS_BAD_NETWORK_PATH:
+    case STATUS_NETWORK_UNREACHABLE:
+    case STATUS_PROTOCOL_UNREACHABLE:
+      return WSAENETUNREACH;
+
+    case STATUS_HOST_UNREACHABLE:
+      return WSAEHOSTUNREACH;
+
+    case STATUS_CANCELLED:
+    case STATUS_REQUEST_ABORTED:
+      return WSAEINTR;
+
+    case STATUS_BUFFER_OVERFLOW:
+    case STATUS_INVALID_BUFFER_SIZE:
+      return WSAEMSGSIZE;
+
+    case STATUS_BUFFER_TOO_SMALL:
+    case STATUS_ACCESS_VIOLATION:
+      return WSAEFAULT;
+
+    case STATUS_DEVICE_NOT_READY:
+    case STATUS_REQUEST_NOT_ACCEPTED:
+      return WSAEWOULDBLOCK;
+
+    case STATUS_INVALID_NETWORK_RESPONSE:
+    case STATUS_NETWORK_BUSY:
+    case STATUS_NO_SUCH_DEVICE:
+    case STATUS_NO_SUCH_FILE:
+    case STATUS_OBJECT_PATH_NOT_FOUND:
+    case STATUS_OBJECT_NAME_NOT_FOUND:
+    case STATUS_UNEXPECTED_NETWORK_ERROR:
+      return WSAENETDOWN;
+
+    case STATUS_INVALID_CONNECTION:
+      return WSAENOTCONN;
+
+    case STATUS_REMOTE_NOT_LISTENING:
+    case STATUS_CONNECTION_REFUSED:
+      return WSAECONNREFUSED;
+
+    case STATUS_PIPE_DISCONNECTED:
+      return WSAESHUTDOWN;
+
+    case STATUS_CONFLICTING_ADDRESSES:
+    case STATUS_INVALID_ADDRESS:
+    case STATUS_INVALID_ADDRESS_COMPONENT:
+      return WSAEADDRNOTAVAIL;
+
+    case STATUS_NOT_SUPPORTED:
+    case STATUS_NOT_IMPLEMENTED:
+      return WSAEOPNOTSUPP;
+
+    case STATUS_ACCESS_DENIED:
+      return WSAEACCES;
+
+    default:
+      if ((status & (FACILITY_NTWIN32 << 16)) == (FACILITY_NTWIN32 << 16) &&
+          (status & (ERROR_SEVERITY_ERROR | ERROR_SEVERITY_WARNING))) {
+        /* It's a windows error that has been previously mapped to an ntstatus
+         * code. */
+        return (DWORD) (status & 0xffff);
+      } else {
+        /* The default fallback for unmappable ntstatus codes. */
+        return WSAEINVAL;
+      }
+  }
+}
+
+
+/*
+ * This function provides a workaround for a bug in the winsock implementation
+ * of WSARecv. The problem is that when SetFileCompletionNotificationModes is
+ * used to avoid IOCP notifications of completed reads, WSARecv does not
+ * reliably indicate whether we can expect a completion package to be posted
+ * when the receive buffer is smaller than the received datagram.
+ *
+ * However it is desirable to use SetFileCompletionNotificationModes because
+ * it yields a massive performance increase.
+ *
+ * This function provides a workaround for that bug, but it only works for the
+ * specific case that we need it for. E.g. it assumes that the "avoid iocp"
+ * bit has been set, and supports only overlapped operation. It also requires
+ * the user to use the default msafd driver, doesn't work when other LSPs are
+ * stacked on top of it.
+ */
+int WSAAPI uv__wsarecv_workaround(SOCKET socket, WSABUF* buffers,
+    DWORD buffer_count, DWORD* bytes, DWORD* flags, WSAOVERLAPPED *overlapped,
+    LPWSAOVERLAPPED_COMPLETION_ROUTINE completion_routine) {
+  NTSTATUS status;
+  void* apc_context;
+  IO_STATUS_BLOCK* iosb = (IO_STATUS_BLOCK*) &overlapped->Internal;
+  AFD_RECV_INFO info;
+  DWORD error;
+
+  if (overlapped == NULL || completion_routine != NULL) {
+    WSASetLastError(WSAEINVAL);
+    return SOCKET_ERROR;
+  }
+
+  info.BufferArray = buffers;
+  info.BufferCount = buffer_count;
+  info.AfdFlags = AFD_OVERLAPPED;
+  info.TdiFlags = TDI_RECEIVE_NORMAL;
+
+  if (*flags & MSG_PEEK) {
+    info.TdiFlags |= TDI_RECEIVE_PEEK;
+  }
+
+  if (*flags & MSG_PARTIAL) {
+    info.TdiFlags |= TDI_RECEIVE_PARTIAL;
+  }
+
+  if (!((intptr_t) overlapped->hEvent & 1)) {
+    apc_context = (void*) overlapped;
+  } else {
+    apc_context = NULL;
+  }
+
+  iosb->Status = STATUS_PENDING;
+  iosb->Pointer = 0;
+
+  status = pNtDeviceIoControlFile((HANDLE) socket,
+                                  overlapped->hEvent,
+                                  NULL,
+                                  apc_context,
+                                  iosb,
+                                  IOCTL_AFD_RECEIVE,
+                                  &info,
+                                  sizeof(info),
+                                  NULL,
+                                  0);
+
+  *flags = 0;
+  *bytes = (DWORD) iosb->Information;
+
+  switch (status) {
+    case STATUS_SUCCESS:
+      error = ERROR_SUCCESS;
+      break;
+
+    case STATUS_PENDING:
+      error = WSA_IO_PENDING;
+      break;
+
+    case STATUS_BUFFER_OVERFLOW:
+      error = WSAEMSGSIZE;
+      break;
+
+    case STATUS_RECEIVE_EXPEDITED:
+      error = ERROR_SUCCESS;
+      *flags = MSG_OOB;
+      break;
+
+    case STATUS_RECEIVE_PARTIAL_EXPEDITED:
+      error = ERROR_SUCCESS;
+      *flags = MSG_PARTIAL | MSG_OOB;
+      break;
+
+    case STATUS_RECEIVE_PARTIAL:
+      error = ERROR_SUCCESS;
+      *flags = MSG_PARTIAL;
+      break;
+
+    default:
+      error = uv__ntstatus_to_winsock_error(status);
+      break;
+  }
+
+  WSASetLastError(error);
+
+  if (error == ERROR_SUCCESS) {
+    return 0;
+  } else {
+    return SOCKET_ERROR;
+  }
+}
+
+
+/* See description of uv__wsarecv_workaround. */
+int WSAAPI uv__wsarecvfrom_workaround(SOCKET socket, WSABUF* buffers,
+    DWORD buffer_count, DWORD* bytes, DWORD* flags, struct sockaddr* addr,
+    int* addr_len, WSAOVERLAPPED *overlapped,
+    LPWSAOVERLAPPED_COMPLETION_ROUTINE completion_routine) {
+  NTSTATUS status;
+  void* apc_context;
+  IO_STATUS_BLOCK* iosb = (IO_STATUS_BLOCK*) &overlapped->Internal;
+  AFD_RECV_DATAGRAM_INFO info;
+  DWORD error;
+
+  if (overlapped == NULL || addr == NULL || addr_len == NULL ||
+      completion_routine != NULL) {
+    WSASetLastError(WSAEINVAL);
+    return SOCKET_ERROR;
+  }
+
+  info.BufferArray = buffers;
+  info.BufferCount = buffer_count;
+  info.AfdFlags = AFD_OVERLAPPED;
+  info.TdiFlags = TDI_RECEIVE_NORMAL;
+  info.Address = addr;
+  info.AddressLength = addr_len;
+
+  if (*flags & MSG_PEEK) {
+    info.TdiFlags |= TDI_RECEIVE_PEEK;
+  }
+
+  if (*flags & MSG_PARTIAL) {
+    info.TdiFlags |= TDI_RECEIVE_PARTIAL;
+  }
+
+  if (!((intptr_t) overlapped->hEvent & 1)) {
+    apc_context = (void*) overlapped;
+  } else {
+    apc_context = NULL;
+  }
+
+  iosb->Status = STATUS_PENDING;
+  iosb->Pointer = 0;
+
+  status = pNtDeviceIoControlFile((HANDLE) socket,
+                                  overlapped->hEvent,
+                                  NULL,
+                                  apc_context,
+                                  iosb,
+                                  IOCTL_AFD_RECEIVE_DATAGRAM,
+                                  &info,
+                                  sizeof(info),
+                                  NULL,
+                                  0);
+
+  *flags = 0;
+  *bytes = (DWORD) iosb->Information;
+
+  switch (status) {
+    case STATUS_SUCCESS:
+      error = ERROR_SUCCESS;
+      break;
+
+    case STATUS_PENDING:
+      error = WSA_IO_PENDING;
+      break;
+
+    case STATUS_BUFFER_OVERFLOW:
+      error = WSAEMSGSIZE;
+      break;
+
+    case STATUS_RECEIVE_EXPEDITED:
+      error = ERROR_SUCCESS;
+      *flags = MSG_OOB;
+      break;
+
+    case STATUS_RECEIVE_PARTIAL_EXPEDITED:
+      error = ERROR_SUCCESS;
+      *flags = MSG_PARTIAL | MSG_OOB;
+      break;
+
+    case STATUS_RECEIVE_PARTIAL:
+      error = ERROR_SUCCESS;
+      *flags = MSG_PARTIAL;
+      break;
+
+    default:
+      error = uv__ntstatus_to_winsock_error(status);
+      break;
+  }
+
+  WSASetLastError(error);
+
+  if (error == ERROR_SUCCESS) {
+    return 0;
+  } else {
+    return SOCKET_ERROR;
+  }
+}
+
+
+int WSAAPI uv__msafd_poll(SOCKET socket, AFD_POLL_INFO* info_in,
+    AFD_POLL_INFO* info_out, OVERLAPPED* overlapped) {
+  IO_STATUS_BLOCK iosb;
+  IO_STATUS_BLOCK* iosb_ptr;
+  HANDLE event = NULL;
+  void* apc_context;
+  NTSTATUS status;
+  DWORD error;
+
+  if (overlapped != NULL) {
+    /* Overlapped operation. */
+    iosb_ptr = (IO_STATUS_BLOCK*) &overlapped->Internal;
+    event = overlapped->hEvent;
+
+    /* Do not report iocp completion if hEvent is tagged. */
+    if ((uintptr_t) event & 1) {
+      event = (HANDLE)((uintptr_t) event & ~(uintptr_t) 1);
+      apc_context = NULL;
+    } else {
+      apc_context = overlapped;
+    }
+
+  } else {
+    /* Blocking operation. */
+    iosb_ptr = &iosb;
+    event = CreateEvent(NULL, FALSE, FALSE, NULL);
+    if (event == NULL) {
+      return SOCKET_ERROR;
+    }
+    apc_context = NULL;
+  }
+
+  iosb_ptr->Status = STATUS_PENDING;
+  status = pNtDeviceIoControlFile((HANDLE) socket,
+                                  event,
+                                  NULL,
+                                  apc_context,
+                                  iosb_ptr,
+                                  IOCTL_AFD_POLL,
+                                  info_in,
+                                  sizeof *info_in,
+                                  info_out,
+                                  sizeof *info_out);
+
+  if (overlapped == NULL) {
+    /* If this is a blocking operation, wait for the event to become signaled,
+     * and then grab the real status from the io status block. */
+    if (status == STATUS_PENDING) {
+      DWORD r = WaitForSingleObject(event, INFINITE);
+
+      if (r == WAIT_FAILED) {
+        DWORD saved_error = GetLastError();
+        CloseHandle(event);
+        WSASetLastError(saved_error);
+        return SOCKET_ERROR;
+      }
+
+      status = iosb.Status;
+    }
+
+    CloseHandle(event);
+  }
+
+  switch (status) {
+    case STATUS_SUCCESS:
+      error = ERROR_SUCCESS;
+      break;
+
+    case STATUS_PENDING:
+      error = WSA_IO_PENDING;
+      break;
+
+    default:
+      error = uv__ntstatus_to_winsock_error(status);
+      break;
+  }
+
+  WSASetLastError(error);
+
+  if (error == ERROR_SUCCESS) {
+    return 0;
+  } else {
+    return SOCKET_ERROR;
+  }
+}
+
+int uv__convert_to_localhost_if_unspecified(const struct sockaddr* addr,
+                                            struct sockaddr_storage* storage) {
+  struct sockaddr_in* dest4;
+  struct sockaddr_in6* dest6;
+
+  if (addr == NULL)
+    return UV_EINVAL;
+
+  switch (addr->sa_family) {
+  case AF_INET:
+    dest4 = (struct sockaddr_in*) storage;
+    memcpy(dest4, addr, sizeof(*dest4));
+    if (dest4->sin_addr.s_addr == 0)
+      dest4->sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+    return 0;
+  case AF_INET6:
+    dest6 = (struct sockaddr_in6*) storage;
+    memcpy(dest6, addr, sizeof(*dest6));
+    if (memcmp(&dest6->sin6_addr,
+               &uv_addr_ip6_any_.sin6_addr,
+               sizeof(uv_addr_ip6_any_.sin6_addr)) == 0) {
+      struct in6_addr init_sin6_addr = IN6ADDR_LOOPBACK_INIT;
+      dest6->sin6_addr = init_sin6_addr;
+    }
+    return 0;
+  default:
+    return UV_EINVAL;
+  }
+}
diff --git a/wpinet/src/main/native/thirdparty/libuv/src/win/winsock.h b/wpinet/src/main/native/thirdparty/libuv/src/win/winsock.h
new file mode 100644
index 0000000..2af9588
--- /dev/null
+++ b/wpinet/src/main/native/thirdparty/libuv/src/win/winsock.h
@@ -0,0 +1,201 @@
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#ifndef UV_WIN_WINSOCK_H_
+#define UV_WIN_WINSOCK_H_
+
+#include <winsock2.h>
+#include <iptypes.h>
+#include <mswsock.h>
+#include <ws2tcpip.h>
+#include <windows.h>
+
+#include "winapi.h"
+
+
+/*
+ * MinGW is missing these too
+ */
+#ifndef SO_UPDATE_CONNECT_CONTEXT
+# define SO_UPDATE_CONNECT_CONTEXT 0x7010
+#endif
+
+#ifndef TCP_KEEPALIVE
+# define TCP_KEEPALIVE 3
+#endif
+
+#ifndef IPV6_V6ONLY
+# define IPV6_V6ONLY 27
+#endif
+
+#ifndef IPV6_HOPLIMIT
+# define IPV6_HOPLIMIT 21
+#endif
+
+#ifndef SIO_BASE_HANDLE
+# define SIO_BASE_HANDLE 0x48000022
+#endif
+
+#ifndef MCAST_JOIN_SOURCE_GROUP
+# define MCAST_JOIN_SOURCE_GROUP 45
+#endif
+
+#ifndef MCAST_LEAVE_SOURCE_GROUP
+# define MCAST_LEAVE_SOURCE_GROUP 46
+#endif
+
+/*
+ * TDI defines that are only in the DDK.
+ * We only need receive flags so far.
+ */
+#ifndef TDI_RECEIVE_NORMAL
+  #define TDI_RECEIVE_BROADCAST           0x00000004
+  #define TDI_RECEIVE_MULTICAST           0x00000008
+  #define TDI_RECEIVE_PARTIAL             0x00000010
+  #define TDI_RECEIVE_NORMAL              0x00000020
+  #define TDI_RECEIVE_EXPEDITED           0x00000040
+  #define TDI_RECEIVE_PEEK                0x00000080
+  #define TDI_RECEIVE_NO_RESPONSE_EXP     0x00000100
+  #define TDI_RECEIVE_COPY_LOOKAHEAD      0x00000200
+  #define TDI_RECEIVE_ENTIRE_MESSAGE      0x00000400
+  #define TDI_RECEIVE_AT_DISPATCH_LEVEL   0x00000800
+  #define TDI_RECEIVE_CONTROL_INFO        0x00001000
+  #define TDI_RECEIVE_FORCE_INDICATION    0x00002000
+  #define TDI_RECEIVE_NO_PUSH             0x00004000
+#endif
+
+/*
+ * The "Auxiliary Function Driver" is the windows kernel-mode driver that does
+ * TCP, UDP etc. Winsock is just a layer that dispatches requests to it.
+ * Having these definitions allows us to bypass winsock and make an AFD kernel
+ * call directly, avoiding a bug in winsock's recvfrom implementation.
+ */
+
+#define AFD_NO_FAST_IO   0x00000001
+#define AFD_OVERLAPPED   0x00000002
+#define AFD_IMMEDIATE    0x00000004
+
+#define AFD_POLL_RECEIVE_BIT            0
+#define AFD_POLL_RECEIVE                (1 << AFD_POLL_RECEIVE_BIT)
+#define AFD_POLL_RECEIVE_EXPEDITED_BIT  1
+#define AFD_POLL_RECEIVE_EXPEDITED      (1 << AFD_POLL_RECEIVE_EXPEDITED_BIT)
+#define AFD_POLL_SEND_BIT               2
+#define AFD_POLL_SEND                   (1 << AFD_POLL_SEND_BIT)
+#define AFD_POLL_DISCONNECT_BIT         3
+#define AFD_POLL_DISCONNECT             (1 << AFD_POLL_DISCONNECT_BIT)
+#define AFD_POLL_ABORT_BIT              4
+#define AFD_POLL_ABORT                  (1 << AFD_POLL_ABORT_BIT)
+#define AFD_POLL_LOCAL_CLOSE_BIT        5
+#define AFD_POLL_LOCAL_CLOSE            (1 << AFD_POLL_LOCAL_CLOSE_BIT)
+#define AFD_POLL_CONNECT_BIT            6
+#define AFD_POLL_CONNECT                (1 << AFD_POLL_CONNECT_BIT)
+#define AFD_POLL_ACCEPT_BIT             7
+#define AFD_POLL_ACCEPT                 (1 << AFD_POLL_ACCEPT_BIT)
+#define AFD_POLL_CONNECT_FAIL_BIT       8
+#define AFD_POLL_CONNECT_FAIL           (1 << AFD_POLL_CONNECT_FAIL_BIT)
+#define AFD_POLL_QOS_BIT                9
+#define AFD_POLL_QOS                    (1 << AFD_POLL_QOS_BIT)
+#define AFD_POLL_GROUP_QOS_BIT          10
+#define AFD_POLL_GROUP_QOS              (1 << AFD_POLL_GROUP_QOS_BIT)
+
+#define AFD_NUM_POLL_EVENTS             11
+#define AFD_POLL_ALL                    ((1 << AFD_NUM_POLL_EVENTS) - 1)
+
+typedef struct _AFD_RECV_DATAGRAM_INFO {
+    LPWSABUF BufferArray;
+    ULONG BufferCount;
+    ULONG AfdFlags;
+    ULONG TdiFlags;
+    struct sockaddr* Address;
+    int* AddressLength;
+} AFD_RECV_DATAGRAM_INFO, *PAFD_RECV_DATAGRAM_INFO;
+
+typedef struct _AFD_RECV_INFO {
+    LPWSABUF BufferArray;
+    ULONG BufferCount;
+    ULONG AfdFlags;
+    ULONG TdiFlags;
+} AFD_RECV_INFO, *PAFD_RECV_INFO;
+
+
+#define _AFD_CONTROL_CODE(operation, method) \
+    ((FSCTL_AFD_BASE) << 12 | (operation << 2) | method)
+
+#define FSCTL_AFD_BASE FILE_DEVICE_NETWORK
+
+#define AFD_RECEIVE            5
+#define AFD_RECEIVE_DATAGRAM   6
+#define AFD_POLL               9
+
+#define IOCTL_AFD_RECEIVE \
+    _AFD_CONTROL_CODE(AFD_RECEIVE, METHOD_NEITHER)
+
+#define IOCTL_AFD_RECEIVE_DATAGRAM \
+    _AFD_CONTROL_CODE(AFD_RECEIVE_DATAGRAM, METHOD_NEITHER)
+
+#define IOCTL_AFD_POLL \
+    _AFD_CONTROL_CODE(AFD_POLL, METHOD_BUFFERED)
+
+#if defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR)
+typedef struct _IP_ADAPTER_UNICAST_ADDRESS_XP {
+  /* FIXME: __C89_NAMELESS was removed */
+  /* __C89_NAMELESS */ union {
+    ULONGLONG Alignment;
+    /* __C89_NAMELESS */ struct {
+      ULONG Length;
+      DWORD Flags;
+    };
+  };
+  struct _IP_ADAPTER_UNICAST_ADDRESS_XP *Next;
+  SOCKET_ADDRESS Address;
+  IP_PREFIX_ORIGIN PrefixOrigin;
+  IP_SUFFIX_ORIGIN SuffixOrigin;
+  IP_DAD_STATE DadState;
+  ULONG ValidLifetime;
+  ULONG PreferredLifetime;
+  ULONG LeaseLifetime;
+} IP_ADAPTER_UNICAST_ADDRESS_XP,*PIP_ADAPTER_UNICAST_ADDRESS_XP;
+
+typedef struct _IP_ADAPTER_UNICAST_ADDRESS_LH {
+  union {
+    ULONGLONG Alignment;
+    struct {
+      ULONG Length;
+      DWORD Flags;
+    };
+  };
+  struct _IP_ADAPTER_UNICAST_ADDRESS_LH *Next;
+  SOCKET_ADDRESS Address;
+  IP_PREFIX_ORIGIN PrefixOrigin;
+  IP_SUFFIX_ORIGIN SuffixOrigin;
+  IP_DAD_STATE DadState;
+  ULONG ValidLifetime;
+  ULONG PreferredLifetime;
+  ULONG LeaseLifetime;
+  UINT8 OnLinkPrefixLength;
+} IP_ADAPTER_UNICAST_ADDRESS_LH,*PIP_ADAPTER_UNICAST_ADDRESS_LH;
+
+#endif
+
+int uv__convert_to_localhost_if_unspecified(const struct sockaddr* addr,
+                                            struct sockaddr_storage* storage);
+
+#endif /* UV_WIN_WINSOCK_H_ */
diff --git a/wpinet/src/main/native/thirdparty/tcpsockets/cpp/TCPAcceptor.cpp b/wpinet/src/main/native/thirdparty/tcpsockets/cpp/TCPAcceptor.cpp
new file mode 100644
index 0000000..fb82ce7
--- /dev/null
+++ b/wpinet/src/main/native/thirdparty/tcpsockets/cpp/TCPAcceptor.cpp
@@ -0,0 +1,211 @@
+/*
+   TCPAcceptor.cpp
+
+   TCPAcceptor class definition. TCPAcceptor provides methods to passively
+   establish TCP/IP connections with clients.
+
+   ------------------------------------------
+
+   Copyright (c) 2013 [Vic Hargrave - http://vichargrave.com]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+*/
+
+#include "wpinet/TCPAcceptor.h"
+
+#include <cstdio>
+#include <cstring>
+
+#ifdef _WIN32
+#define _WINSOCK_DEPRECATED_NO_WARNINGS
+#include <WinSock2.h>
+#include <Ws2tcpip.h>
+#pragma comment(lib, "Ws2_32.lib")
+#else
+#include <arpa/inet.h>
+#include <fcntl.h>
+#include <netinet/in.h>
+#include <unistd.h>
+#endif
+
+#include <wpi/Logger.h>
+#include <wpi/SmallString.h>
+
+#include "wpinet/SocketError.h"
+
+using namespace wpi;
+
+TCPAcceptor::TCPAcceptor(int port, std::string_view address, Logger& logger)
+    : m_lsd(0),
+      m_port(port),
+      m_address(address),
+      m_listening(false),
+      m_logger(logger) {
+  m_shutdown = false;
+#ifdef _WIN32
+  WSAData wsaData;
+  WORD wVersionRequested = MAKEWORD(2, 2);
+  (void)WSAStartup(wVersionRequested, &wsaData);
+#endif
+}
+
+TCPAcceptor::~TCPAcceptor() {
+  if (m_lsd > 0) {
+    shutdown();
+#ifdef _WIN32
+    closesocket(m_lsd);
+#else
+    close(m_lsd);
+#endif
+  }
+#ifdef _WIN32
+  WSACleanup();
+#endif
+}
+
+int TCPAcceptor::start() {
+  if (m_listening) {
+    return 0;
+  }
+
+  m_lsd = socket(PF_INET, SOCK_STREAM, 0);
+  if (m_lsd < 0) {
+    WPI_ERROR(m_logger, "could not create socket");
+    return -1;
+  }
+  struct sockaddr_in address;
+
+  std::memset(&address, 0, sizeof(address));
+  address.sin_family = PF_INET;
+  if (m_address.size() > 0) {
+#ifdef _WIN32
+    SmallString<128> addr_copy(m_address);
+    addr_copy.push_back('\0');
+    int res = InetPton(PF_INET, addr_copy.data(), &(address.sin_addr));
+#else
+    int res = inet_pton(PF_INET, m_address.c_str(), &(address.sin_addr));
+#endif
+    if (res != 1) {
+      WPI_ERROR(m_logger, "could not resolve {} address", m_address);
+      return -1;
+    }
+  } else {
+    address.sin_addr.s_addr = INADDR_ANY;
+  }
+  address.sin_port = htons(m_port);
+
+#ifdef _WIN32
+  int optval = 1;
+  setsockopt(m_lsd, SOL_SOCKET, SO_EXCLUSIVEADDRUSE,
+             reinterpret_cast<char*>(&optval), sizeof optval);
+#else
+  int optval = 1;
+  setsockopt(m_lsd, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast<char*>(&optval),
+             sizeof optval);
+#endif
+
+  int result = bind(m_lsd, reinterpret_cast<struct sockaddr*>(&address),
+                    sizeof(address));
+  if (result != 0) {
+    WPI_ERROR(m_logger, "bind() to port {} failed: {}", m_port,
+              SocketStrerror());
+    return result;
+  }
+
+  result = listen(m_lsd, 5);
+  if (result != 0) {
+    WPI_ERROR(m_logger, "listen() on port {} failed: {}", m_port,
+              SocketStrerror());
+    return result;
+  }
+  m_listening = true;
+  return result;
+}
+
+void TCPAcceptor::shutdown() {
+  m_shutdown = true;
+#ifdef _WIN32
+  ::shutdown(m_lsd, SD_BOTH);
+
+  // this is ugly, but the easiest way to do this
+  // force wakeup of accept() with a non-blocking connect to ourselves
+  struct sockaddr_in address;
+
+  std::memset(&address, 0, sizeof(address));
+  address.sin_family = PF_INET;
+  SmallString<128> addr_copy;
+  if (m_address.size() > 0)
+    addr_copy = m_address;
+  else
+    addr_copy = "127.0.0.1";
+  addr_copy.push_back('\0');
+  int size = sizeof(address);
+  if (WSAStringToAddress(addr_copy.data(), PF_INET, nullptr,
+                         (struct sockaddr*)&address, &size) != 0)
+    return;
+  address.sin_port = htons(m_port);
+
+  int result = -1, sd = socket(AF_INET, SOCK_STREAM, 0);
+  if (sd < 0)
+    return;
+
+  // Set socket to non-blocking
+  u_long mode = 1;
+  ioctlsocket(sd, FIONBIO, &mode);
+
+  // Try to connect
+  ::connect(sd, (struct sockaddr*)&address, sizeof(address));
+
+  // Close
+  ::closesocket(sd);
+
+#else
+  ::shutdown(m_lsd, SHUT_RDWR);
+  int nullfd = ::open("/dev/null", O_RDONLY);
+  if (nullfd >= 0) {
+    ::dup2(nullfd, m_lsd);
+    ::close(nullfd);
+  }
+#endif
+}
+
+std::unique_ptr<NetworkStream> TCPAcceptor::accept() {
+  if (!m_listening || m_shutdown) {
+    return nullptr;
+  }
+
+  struct sockaddr_in address;
+#ifdef _WIN32
+  int len = sizeof(address);
+#else
+  socklen_t len = sizeof(address);
+#endif
+  std::memset(&address, 0, sizeof(address));
+  int sd = ::accept(m_lsd, reinterpret_cast<struct sockaddr*>(&address), &len);
+  if (sd < 0) {
+    if (!m_shutdown) {
+      WPI_ERROR(m_logger, "accept() on port {} failed: {}", m_port,
+                SocketStrerror());
+    }
+    return nullptr;
+  }
+  if (m_shutdown) {
+#ifdef _WIN32
+    closesocket(sd);
+#else
+    close(sd);
+#endif
+    return nullptr;
+  }
+  return std::unique_ptr<NetworkStream>(new TCPStream(sd, &address));
+}
diff --git a/wpinet/src/main/native/thirdparty/tcpsockets/cpp/TCPConnector.cpp b/wpinet/src/main/native/thirdparty/tcpsockets/cpp/TCPConnector.cpp
new file mode 100644
index 0000000..52b9c98
--- /dev/null
+++ b/wpinet/src/main/native/thirdparty/tcpsockets/cpp/TCPConnector.cpp
@@ -0,0 +1,219 @@
+/*
+   TCPConnector.h
+
+   TCPConnector class definition. TCPConnector provides methods to actively
+   establish TCP/IP connections with a server.
+
+   ------------------------------------------
+
+   Copyright (c) 2013 [Vic Hargrave - http://vichargrave.com]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License
+*/
+
+#include "wpinet/TCPConnector.h"
+
+#include <fcntl.h>
+
+#include <cerrno>
+#include <cstdio>
+#include <cstring>
+
+#ifdef _WIN32
+#include <WS2tcpip.h>
+#include <WinSock2.h>
+#else
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <sys/select.h>
+#include <unistd.h>
+#endif
+
+#include <wpi/Logger.h>
+#include <wpi/SmallString.h>
+
+#include "wpinet/SocketError.h"
+#include "wpinet/TCPStream.h"
+
+using namespace wpi;
+
+static int ResolveHostName(const char* hostname, struct in_addr* addr) {
+  struct addrinfo hints;
+  struct addrinfo* res;
+
+  hints.ai_flags = 0;
+  hints.ai_family = AF_INET;
+  hints.ai_socktype = SOCK_STREAM;
+  hints.ai_protocol = 0;
+  hints.ai_addrlen = 0;
+  hints.ai_addr = nullptr;
+  hints.ai_canonname = nullptr;
+  hints.ai_next = nullptr;
+  int result = getaddrinfo(hostname, nullptr, &hints, &res);
+  if (result == 0) {
+    std::memcpy(
+        addr, &(reinterpret_cast<struct sockaddr_in*>(res->ai_addr)->sin_addr),
+        sizeof(struct in_addr));
+    freeaddrinfo(res);
+  }
+  return result;
+}
+
+std::unique_ptr<NetworkStream> TCPConnector::connect(const char* server,
+                                                     int port, Logger& logger,
+                                                     int timeout) {
+#ifdef _WIN32
+  struct WSAHelper {
+    WSAHelper() {
+      WSAData wsaData;
+      WORD wVersionRequested = MAKEWORD(2, 2);
+      WSAStartup(wVersionRequested, &wsaData);
+    }
+    ~WSAHelper() { WSACleanup(); }
+  };
+  static WSAHelper helper;
+#endif
+  struct sockaddr_in address;
+
+  std::memset(&address, 0, sizeof(address));
+  address.sin_family = AF_INET;
+  if (ResolveHostName(server, &(address.sin_addr)) != 0) {
+#ifdef _WIN32
+    SmallString<128> addr_copy(server);
+    addr_copy.push_back('\0');
+    int res = InetPton(PF_INET, addr_copy.data(), &(address.sin_addr));
+#else
+    int res = inet_pton(PF_INET, server, &(address.sin_addr));
+#endif
+    if (res != 1) {
+      WPI_ERROR(logger, "could not resolve {} address", server);
+      return nullptr;
+    }
+  }
+  address.sin_port = htons(port);
+
+  if (timeout == 0) {
+    int sd = socket(AF_INET, SOCK_STREAM, 0);
+    if (sd < 0) {
+      WPI_ERROR(logger, "could not create socket");
+      return nullptr;
+    }
+    if (::connect(sd, reinterpret_cast<struct sockaddr*>(&address),
+                  sizeof(address)) != 0) {
+      WPI_ERROR(logger, "connect() to {} port {} failed: {}", server, port,
+                SocketStrerror());
+#ifdef _WIN32
+      closesocket(sd);
+#else
+      ::close(sd);
+#endif
+      return nullptr;
+    }
+    return std::unique_ptr<NetworkStream>(new TCPStream(sd, &address));
+  }
+
+  fd_set sdset;
+  struct timeval tv;
+  socklen_t len;
+  int result = -1, valopt, sd = socket(AF_INET, SOCK_STREAM, 0);
+  if (sd < 0) {
+    WPI_ERROR(logger, "could not create socket");
+    return nullptr;
+  }
+
+// Set socket to non-blocking
+#ifdef _WIN32
+  u_long mode = 1;
+  if (ioctlsocket(sd, FIONBIO, &mode) == SOCKET_ERROR)
+    WPI_WARNING(logger, "could not set socket to non-blocking: {}",
+                SocketStrerror());
+#else
+  int arg;
+  arg = fcntl(sd, F_GETFL, nullptr);
+  if (arg < 0) {
+    WPI_WARNING(logger, "could not set socket to non-blocking: {}",
+                SocketStrerror());
+  } else {
+    arg |= O_NONBLOCK;
+    if (fcntl(sd, F_SETFL, arg) < 0) {
+      WPI_WARNING(logger, "could not set socket to non-blocking: {}",
+                  SocketStrerror());
+    }
+  }
+#endif
+
+  // Connect with time limit
+  if ((result = ::connect(sd, reinterpret_cast<struct sockaddr*>(&address),
+                          sizeof(address))) < 0) {
+    int my_errno = SocketErrno();
+#ifdef _WIN32
+    if (my_errno == WSAEWOULDBLOCK || my_errno == WSAEINPROGRESS) {
+#else
+    if (my_errno == EWOULDBLOCK || my_errno == EINPROGRESS) {
+#endif
+      tv.tv_sec = timeout;
+      tv.tv_usec = 0;
+      FD_ZERO(&sdset);
+      FD_SET(sd, &sdset);
+      if (select(sd + 1, nullptr, &sdset, nullptr, &tv) > 0) {
+        len = sizeof(int);
+        getsockopt(sd, SOL_SOCKET, SO_ERROR, reinterpret_cast<char*>(&valopt),
+                   &len);
+        if (valopt) {
+          WPI_ERROR(logger, "select() to {} port {} error {} - {}", server,
+                    port, valopt, SocketStrerror(valopt));
+        } else {
+          // connection established
+          result = 0;
+        }
+      } else {
+        WPI_INFO(logger, "connect() to {} port {} timed out", server, port);
+      }
+    } else {
+      WPI_ERROR(logger, "connect() to {} port {} error {} - {}", server, port,
+                SocketErrno(), SocketStrerror());
+    }
+  }
+
+// Return socket to blocking mode
+#ifdef _WIN32
+  mode = 0;
+  if (ioctlsocket(sd, FIONBIO, &mode) == SOCKET_ERROR)
+    WPI_WARNING(logger, "could not set socket to blocking: {}",
+                SocketStrerror());
+#else
+  arg = fcntl(sd, F_GETFL, nullptr);
+  if (arg < 0) {
+    WPI_WARNING(logger, "could not set socket to blocking: {}",
+                SocketStrerror());
+  } else {
+    arg &= (~O_NONBLOCK);
+    if (fcntl(sd, F_SETFL, arg) < 0) {
+      WPI_WARNING(logger, "could not set socket to blocking: {}",
+                  SocketStrerror());
+    }
+  }
+#endif
+
+  // Create stream object if connected, close if not.
+  if (result == -1) {
+#ifdef _WIN32
+    closesocket(sd);
+#else
+    ::close(sd);
+#endif
+    return nullptr;
+  }
+  return std::unique_ptr<NetworkStream>(new TCPStream(sd, &address));
+}
diff --git a/wpinet/src/main/native/thirdparty/tcpsockets/cpp/TCPConnector_parallel.cpp b/wpinet/src/main/native/thirdparty/tcpsockets/cpp/TCPConnector_parallel.cpp
new file mode 100644
index 0000000..1d979cb
--- /dev/null
+++ b/wpinet/src/main/native/thirdparty/tcpsockets/cpp/TCPConnector_parallel.cpp
@@ -0,0 +1,131 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#include "wpinet/TCPConnector.h"  // NOLINT(build/include_order)
+
+#include <atomic>
+#include <chrono>
+#include <thread>
+#include <tuple>
+
+#include <wpi/SmallSet.h>
+#include <wpi/condition_variable.h>
+#include <wpi/mutex.h>
+
+using namespace wpi;
+
+// MSVC < 1900 doesn't have support for thread_local
+#if !defined(_MSC_VER) || _MSC_VER >= 1900
+// clang check for availability of thread_local
+#if !defined(__has_feature) || __has_feature(cxx_thread_local)
+#define HAVE_THREAD_LOCAL
+#endif
+#endif
+
+std::unique_ptr<NetworkStream> TCPConnector::connect_parallel(
+    std::span<const std::pair<const char*, int>> servers, Logger& logger,
+    int timeout) {
+  if (servers.empty()) {
+    return nullptr;
+  }
+
+  // structure to make sure we don't start duplicate workers
+  struct GlobalState {
+    wpi::mutex mtx;
+#ifdef HAVE_THREAD_LOCAL
+    SmallSet<std::pair<std::string, int>, 16> active;
+#else
+    SmallSet<std::tuple<std::thread::id, std::string, int>, 16> active;
+#endif
+  };
+#ifdef HAVE_THREAD_LOCAL
+  thread_local auto global = std::make_shared<GlobalState>();
+#else
+  static auto global = std::make_shared<GlobalState>();
+  auto this_id = std::this_thread::get_id();
+#endif
+  auto local = global;  // copy to an automatic variable for lambda capture
+
+  // structure shared between threads and this function
+  struct Result {
+    wpi::mutex mtx;
+    wpi::condition_variable cv;
+    std::unique_ptr<NetworkStream> stream;
+    std::atomic<unsigned int> count{0};
+    std::atomic<bool> done{false};
+  };
+  auto result = std::make_shared<Result>();
+
+  // start worker threads; this is I/O bound so we don't limit to # of procs
+  Logger* plogger = &logger;
+  unsigned int num_workers = 0;
+  for (const auto& server : servers) {
+    std::pair<std::string, int> server_copy{std::string{server.first},
+                                            server.second};
+#ifdef HAVE_THREAD_LOCAL
+    const auto& active_tracker = server_copy;
+#else
+    std::tuple<std::thread::id, std::string, int> active_tracker{
+        this_id, server_copy.first, server_copy.second};
+#endif
+
+    // don't start a new worker if we had a previously still-active connection
+    // attempt to the same server
+    {
+      std::scoped_lock lock(local->mtx);
+      if (local->active.count(active_tracker) > 0) {
+        continue;  // already in set
+      }
+    }
+
+    ++num_workers;
+
+    // start the worker
+    std::thread([=] {
+      if (!result->done) {
+        // add to global state
+        {
+          std::scoped_lock lock(local->mtx);
+          local->active.insert(active_tracker);
+        }
+
+        // try to connect
+        auto stream = connect(server_copy.first.c_str(), server_copy.second,
+                              *plogger, timeout);
+
+        // remove from global state
+        {
+          std::scoped_lock lock(local->mtx);
+          local->active.erase(active_tracker);
+        }
+
+        // successful connection
+        if (stream) {
+          std::scoped_lock lock(result->mtx);
+          if (!result->done.exchange(true)) {
+            result->stream = std::move(stream);
+          }
+        }
+      }
+      ++result->count;
+      result->cv.notify_all();
+    }).detach();
+  }
+
+  // wait for a result, timeout, or all finished
+  std::unique_lock lock(result->mtx);
+  if (timeout == 0) {
+    result->cv.wait(
+        lock, [&] { return result->stream || result->count >= num_workers; });
+  } else {
+    auto timeout_time =
+        std::chrono::steady_clock::now() + std::chrono::seconds(timeout);
+    result->cv.wait_until(lock, timeout_time, [&] {
+      return result->stream || result->count >= num_workers;
+    });
+  }
+
+  // no need to wait for remaining worker threads; shared_ptr will clean up
+  return std::move(result->stream);
+}
diff --git a/wpinet/src/main/native/thirdparty/tcpsockets/cpp/TCPStream.cpp b/wpinet/src/main/native/thirdparty/tcpsockets/cpp/TCPStream.cpp
new file mode 100644
index 0000000..920f7b1
--- /dev/null
+++ b/wpinet/src/main/native/thirdparty/tcpsockets/cpp/TCPStream.cpp
@@ -0,0 +1,230 @@
+/*
+   TCPStream.h
+
+   TCPStream class definition. TCPStream provides methods to transfer
+   data between peers over a TCP/IP connection.
+
+   ------------------------------------------
+
+   Copyright (c) 2013 [Vic Hargrave - http://vichargrave.com]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+*/
+
+#include "wpinet/TCPStream.h"
+
+#include <fcntl.h>
+
+#ifdef _WIN32
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#else
+#include <arpa/inet.h>
+#include <netinet/tcp.h>
+#include <unistd.h>
+#endif
+
+#include <cerrno>
+
+using namespace wpi;
+
+TCPStream::TCPStream(int sd, sockaddr_in* address)
+    : m_sd(sd), m_blocking(true) {
+  char ip[50];
+#ifdef _WIN32
+  InetNtop(PF_INET, &(address->sin_addr.s_addr), ip, sizeof(ip) - 1);
+#else
+  inet_ntop(PF_INET, reinterpret_cast<in_addr*>(&(address->sin_addr.s_addr)),
+            ip, sizeof(ip) - 1);
+#ifdef SO_NOSIGPIPE
+  // disable SIGPIPE on Mac OS X
+  int set = 1;
+  setsockopt(m_sd, SOL_SOCKET, SO_NOSIGPIPE, reinterpret_cast<char*>(&set),
+             sizeof set);
+#endif
+#endif
+  m_peerIP = ip;
+  m_peerPort = ntohs(address->sin_port);
+}
+
+TCPStream::~TCPStream() {
+  close();
+}
+
+size_t TCPStream::send(const char* buffer, size_t len, Error* err) {
+  if (m_sd < 0) {
+    *err = kConnectionClosed;
+    return 0;
+  }
+#ifdef _WIN32
+  WSABUF wsaBuf;
+  wsaBuf.buf = const_cast<char*>(buffer);
+  wsaBuf.len = (ULONG)len;
+  DWORD rv;
+  bool result = true;
+  while (WSASend(m_sd, &wsaBuf, 1, &rv, 0, nullptr, nullptr) == SOCKET_ERROR) {
+    if (WSAGetLastError() != WSAEWOULDBLOCK) {
+      result = false;
+      break;
+    }
+    if (!m_blocking) {
+      *err = kWouldBlock;
+      return 0;
+    }
+    Sleep(1);
+  }
+  if (!result) {
+    char Buffer[128];
+#ifdef _MSC_VER
+    sprintf_s(Buffer, "Send() failed: WSA error=%d\n", WSAGetLastError());
+#else
+    std::snprintf(Buffer, sizeof(Buffer), "Send() failed: WSA error=%d\n",
+                  WSAGetLastError());
+#endif
+    OutputDebugStringA(Buffer);
+    *err = kConnectionReset;
+    return 0;
+  }
+#else
+#ifdef MSG_NOSIGNAL
+  // disable SIGPIPE on Linux
+  ssize_t rv = ::send(m_sd, buffer, len, MSG_NOSIGNAL);
+#else
+  ssize_t rv = ::send(m_sd, buffer, len, 0);
+#endif
+  if (rv < 0) {
+    if (!m_blocking && (errno == EAGAIN || errno == EWOULDBLOCK)) {
+      *err = kWouldBlock;
+    } else {
+      *err = kConnectionReset;
+    }
+    return 0;
+  }
+#endif
+  return static_cast<size_t>(rv);
+}
+
+size_t TCPStream::receive(char* buffer, size_t len, Error* err, int timeout) {
+  if (m_sd < 0) {
+    *err = kConnectionClosed;
+    return 0;
+  }
+#ifdef _WIN32
+  int rv;
+#else
+  ssize_t rv;
+#endif
+  if (timeout <= 0) {
+#ifdef _WIN32
+    rv = recv(m_sd, buffer, len, 0);
+#else
+    rv = read(m_sd, buffer, len);
+#endif
+  } else if (WaitForReadEvent(timeout)) {
+#ifdef _WIN32
+    rv = recv(m_sd, buffer, len, 0);
+#else
+    rv = read(m_sd, buffer, len);
+#endif
+  } else {
+    *err = kConnectionTimedOut;
+    return 0;
+  }
+  if (rv < 0) {
+#ifdef _WIN32
+    if (!m_blocking && WSAGetLastError() == WSAEWOULDBLOCK) {
+#else
+    if (!m_blocking && (errno == EAGAIN || errno == EWOULDBLOCK)) {
+#endif
+      *err = kWouldBlock;
+    } else {
+      *err = kConnectionReset;
+    }
+    return 0;
+  }
+  return static_cast<size_t>(rv);
+}
+
+void TCPStream::close() {
+  if (m_sd >= 0) {
+#ifdef _WIN32
+    ::shutdown(m_sd, SD_BOTH);
+    closesocket(m_sd);
+#else
+    ::shutdown(m_sd, SHUT_RDWR);
+    ::close(m_sd);
+#endif
+  }
+  m_sd = -1;
+}
+
+std::string_view TCPStream::getPeerIP() const {
+  return m_peerIP;
+}
+
+int TCPStream::getPeerPort() const {
+  return m_peerPort;
+}
+
+void TCPStream::setNoDelay() {
+  if (m_sd < 0) {
+    return;
+  }
+  int optval = 1;
+  setsockopt(m_sd, IPPROTO_TCP, TCP_NODELAY, reinterpret_cast<char*>(&optval),
+             sizeof optval);
+}
+
+bool TCPStream::setBlocking(bool enabled) {
+  if (m_sd < 0) {
+    return true;  // silently accept
+  }
+#ifdef _WIN32
+  u_long mode = enabled ? 0 : 1;
+  if (ioctlsocket(m_sd, FIONBIO, &mode) == SOCKET_ERROR) {
+    return false;
+  }
+#else
+  int flags = fcntl(m_sd, F_GETFL, nullptr);
+  if (flags < 0) {
+    return false;
+  }
+  if (enabled) {
+    flags &= ~O_NONBLOCK;
+  } else {
+    flags |= O_NONBLOCK;
+  }
+  if (fcntl(m_sd, F_SETFL, flags) < 0) {
+    return false;
+  }
+#endif
+  return true;
+}
+
+int TCPStream::getNativeHandle() const {
+  return m_sd;
+}
+
+bool TCPStream::WaitForReadEvent(int timeout) {
+  fd_set sdset;
+  struct timeval tv;
+
+  tv.tv_sec = timeout;
+  tv.tv_usec = 0;
+  FD_ZERO(&sdset);
+  FD_SET(m_sd, &sdset);
+  if (select(m_sd + 1, &sdset, nullptr, nullptr, &tv) > 0) {
+    return true;
+  }
+  return false;
+}
diff --git a/wpinet/src/main/native/thirdparty/tcpsockets/include/wpinet/TCPAcceptor.h b/wpinet/src/main/native/thirdparty/tcpsockets/include/wpinet/TCPAcceptor.h
new file mode 100644
index 0000000..5ba7ce5
--- /dev/null
+++ b/wpinet/src/main/native/thirdparty/tcpsockets/include/wpinet/TCPAcceptor.h
@@ -0,0 +1,58 @@
+/*
+   TCPAcceptor.h
+
+   TCPAcceptor class interface. TCPAcceptor provides methods to passively
+   establish TCP/IP connections with clients.
+
+   ------------------------------------------
+
+   Copyright (c) 2013 [Vic Hargrave - http://vichargrave.com]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+*/
+
+#ifndef WPINET_TCPACCEPTOR_H_
+#define WPINET_TCPACCEPTOR_H_
+
+#include <atomic>
+#include <memory>
+#include <string>
+#include <string_view>
+
+#include "wpinet/NetworkAcceptor.h"
+#include "wpinet/TCPStream.h"
+
+namespace wpi {
+
+class Logger;
+
+class TCPAcceptor : public NetworkAcceptor {
+  int m_lsd;
+  int m_port;
+  std::string m_address;
+  bool m_listening;
+  std::atomic_bool m_shutdown;
+  Logger& m_logger;
+
+ public:
+  TCPAcceptor(int port, std::string_view address, Logger& logger);
+  ~TCPAcceptor() override;
+
+  int start() override;
+  void shutdown() final;
+  std::unique_ptr<NetworkStream> accept() override;
+};
+
+}  // namespace wpi
+
+#endif  // WPINET_TCPACCEPTOR_H_
diff --git a/wpinet/src/main/native/thirdparty/tcpsockets/include/wpinet/TCPConnector.h b/wpinet/src/main/native/thirdparty/tcpsockets/include/wpinet/TCPConnector.h
new file mode 100644
index 0000000..d9c3ad9
--- /dev/null
+++ b/wpinet/src/main/native/thirdparty/tcpsockets/include/wpinet/TCPConnector.h
@@ -0,0 +1,49 @@
+/*
+   TCPConnector.h
+
+   TCPConnector class interface. TCPConnector provides methods to actively
+   establish TCP/IP connections with a server.
+
+   ------------------------------------------
+
+   Copyright (c) 2013 [Vic Hargrave - http://vichargrave.com]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License
+*/
+
+#ifndef WPINET_TCPCONNECTOR_H_
+#define WPINET_TCPCONNECTOR_H_
+
+#include <memory>
+#include <span>
+#include <utility>
+
+#include "wpinet/NetworkStream.h"
+
+namespace wpi {
+
+class Logger;
+
+class TCPConnector {
+ public:
+  static std::unique_ptr<NetworkStream> connect(const char* server, int port,
+                                                Logger& logger,
+                                                int timeout = 0);
+  static std::unique_ptr<NetworkStream> connect_parallel(
+      std::span<const std::pair<const char*, int>> servers, Logger& logger,
+      int timeout = 0);
+};
+
+}  // namespace wpi
+
+#endif  // WPINET_TCPCONNECTOR_H_
diff --git a/wpinet/src/main/native/thirdparty/tcpsockets/include/wpinet/TCPStream.h b/wpinet/src/main/native/thirdparty/tcpsockets/include/wpinet/TCPStream.h
new file mode 100644
index 0000000..f54da48
--- /dev/null
+++ b/wpinet/src/main/native/thirdparty/tcpsockets/include/wpinet/TCPStream.h
@@ -0,0 +1,72 @@
+/*
+   TCPStream.h
+
+   TCPStream class interface. TCPStream provides methods to transfer
+   data between peers over a TCP/IP connection.
+
+   ------------------------------------------
+
+   Copyright (c) 2013 [Vic Hargrave - http://vichargrave.com]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+*/
+
+#ifndef WPINET_TCPSTREAM_H_
+#define WPINET_TCPSTREAM_H_
+
+#include <cstddef>
+#include <string>
+#include <string_view>
+
+#include "wpinet/NetworkStream.h"
+
+struct sockaddr_in;
+
+namespace wpi {
+
+class TCPStream : public NetworkStream {
+  int m_sd;
+  std::string m_peerIP;
+  int m_peerPort;
+  bool m_blocking;
+
+ public:
+  friend class TCPAcceptor;
+  friend class TCPConnector;
+
+  ~TCPStream() override;
+
+  size_t send(const char* buffer, size_t len, Error* err) override;
+  size_t receive(char* buffer, size_t len, Error* err,
+                 int timeout = 0) override;
+  void close() final;
+
+  std::string_view getPeerIP() const override;
+  int getPeerPort() const override;
+  void setNoDelay() override;
+  bool setBlocking(bool enabled) override;
+  int getNativeHandle() const override;
+
+  TCPStream(const TCPStream& stream) = delete;
+  TCPStream& operator=(const TCPStream&) = delete;
+
+ private:
+  bool WaitForReadEvent(int timeout);
+
+  TCPStream(int sd, sockaddr_in* address);
+  TCPStream() = delete;
+};
+
+}  // namespace wpi
+
+#endif  // WPINET_TCPSTREAM_H_
diff --git a/wpinet/src/main/native/windows/DynamicDns.cpp b/wpinet/src/main/native/windows/DynamicDns.cpp
new file mode 100644
index 0000000..c59d554
--- /dev/null
+++ b/wpinet/src/main/native/windows/DynamicDns.cpp
@@ -0,0 +1,41 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#include "DynamicDns.h"
+
+using namespace wpi;
+
+DynamicDns& DynamicDns::GetDynamicDns() {
+  static DynamicDns dns;
+  return dns;
+}
+
+DynamicDns::DynamicDns() {
+  HMODULE library = LoadLibraryW(L"dnsapi");
+
+  if (library == nullptr) {
+    return;
+  }
+
+  DnsServiceFreeInstancePtr = (DnsServiceFreeInstanceFunc)GetProcAddress(
+      library, "DnsServiceFreeInstance");
+  DnsServiceConstructInstancePtr =
+      (DnsServiceConstructInstanceFunc)GetProcAddress(
+          library, "DnsServiceConstructInstance");
+  DnsServiceRegisterPtr =
+      (DnsServiceRegisterFunc)GetProcAddress(library, "DnsServiceRegister");
+  DnsServiceDeRegisterPtr =
+      (DnsServiceDeRegisterFunc)GetProcAddress(library, "DnsServiceDeRegister");
+
+  CanDnsAnnounce = DnsServiceFreeInstancePtr &&
+                   DnsServiceConstructInstancePtr && DnsServiceRegisterPtr &&
+                   DnsServiceDeRegisterPtr;
+
+  DnsServiceBrowsePtr =
+      (DnsServiceBrowseFunc)GetProcAddress(library, "DnsServiceBrowse");
+  DnsServiceBrowseCancelPtr = (DnsServiceBrowseCancelFunc)GetProcAddress(
+      library, "DnsServiceBrowseCancel");
+
+  CanDnsResolve = DnsServiceBrowsePtr && DnsServiceBrowseCancelPtr;
+}
diff --git a/wpinet/src/main/native/windows/DynamicDns.h b/wpinet/src/main/native/windows/DynamicDns.h
new file mode 100644
index 0000000..c8bee04
--- /dev/null
+++ b/wpinet/src/main/native/windows/DynamicDns.h
@@ -0,0 +1,59 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#pragma once
+
+#ifndef UNICODE
+#define UNICODE
+#endif
+
+#include <windows.h>
+
+#include <WinDNS.h>
+
+namespace wpi {
+class DynamicDns {
+ public:
+  using DnsServiceFreeInstanceFunc =
+      VOID(WINAPI*)(_In_ PDNS_SERVICE_INSTANCE pInstance);
+
+  using DnsServiceConstructInstanceFunc = PDNS_SERVICE_INSTANCE(WINAPI*)(
+      _In_ PCWSTR pServiceName, _In_ PCWSTR pHostName,
+      _In_opt_ PIP4_ADDRESS pIp4, _In_opt_ PIP6_ADDRESS pIp6, _In_ WORD wPort,
+      _In_ WORD wPriority, _In_ WORD wWeight, _In_ DWORD dwPropertiesCount,
+      _In_reads_(dwPropertiesCount) PCWSTR* keys,
+      _In_reads_(dwPropertiesCount) PCWSTR* values);
+
+  using DnsServiceRegisterFunc =
+      DWORD(WINAPI*)(_In_ PDNS_SERVICE_REGISTER_REQUEST pRequest,
+                     _Inout_opt_ PDNS_SERVICE_CANCEL pCancel);
+
+  using DnsServiceDeRegisterFunc =
+      DWORD(WINAPI*)(_In_ PDNS_SERVICE_REGISTER_REQUEST pRequest,
+                     _Inout_opt_ PDNS_SERVICE_CANCEL pCancel);
+
+  using DnsServiceBrowseFunc =
+      DNS_STATUS(WINAPI*)(_In_ PDNS_SERVICE_BROWSE_REQUEST pRequest,
+                          _Inout_ PDNS_SERVICE_CANCEL pCancel);
+
+  using DnsServiceBrowseCancelFunc =
+      DNS_STATUS(WINAPI*)(_In_ PDNS_SERVICE_CANCEL pCancelHandle);
+
+  DnsServiceBrowseFunc DnsServiceBrowsePtr{nullptr};
+  DnsServiceBrowseCancelFunc DnsServiceBrowseCancelPtr{nullptr};
+
+  DnsServiceFreeInstanceFunc DnsServiceFreeInstancePtr{nullptr};
+  DnsServiceConstructInstanceFunc DnsServiceConstructInstancePtr{nullptr};
+  DnsServiceRegisterFunc DnsServiceRegisterPtr{nullptr};
+  DnsServiceDeRegisterFunc DnsServiceDeRegisterPtr{nullptr};
+
+  bool CanDnsAnnounce{false};
+  bool CanDnsResolve{false};
+
+  static DynamicDns& GetDynamicDns();
+
+ private:
+  DynamicDns();
+};
+}  // namespace wpi
diff --git a/wpinet/src/main/native/windows/MulticastServiceAnnouncer.cpp b/wpinet/src/main/native/windows/MulticastServiceAnnouncer.cpp
new file mode 100644
index 0000000..c1ed58d
--- /dev/null
+++ b/wpinet/src/main/native/windows/MulticastServiceAnnouncer.cpp
@@ -0,0 +1,219 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#ifndef UNICODE
+#define UNICODE
+#endif
+
+#include "wpinet/MulticastServiceAnnouncer.h"
+
+#include <string>
+#include <vector>
+
+#include <wpi/ConvertUTF.h>
+#include <wpi/SmallString.h>
+#include <wpi/SmallVector.h>
+#include <wpi/StringExtras.h>
+
+#include "DynamicDns.h"
+#include "wpinet/hostname.h"
+
+using namespace wpi;
+
+struct ImplBase {
+  wpi::DynamicDns& dynamicDns = wpi::DynamicDns::GetDynamicDns();
+  PDNS_SERVICE_INSTANCE serviceInstance = nullptr;
+  HANDLE event = nullptr;
+};
+
+struct MulticastServiceAnnouncer::Impl : ImplBase {
+  std::wstring serviceType;
+  std::wstring serviceInstanceName;
+  std::wstring hostName;
+  int port;
+  std::vector<std::wstring> keys;
+  std::vector<PCWSTR> keyPtrs;
+  std::vector<std::wstring> values;
+  std::vector<PCWSTR> valuePtrs;
+
+  template <typename T>
+  Impl(std::string_view serviceName, std::string_view serviceType, int port,
+       std::span<const std::pair<T, T>> txt);
+};
+
+template <typename T>
+MulticastServiceAnnouncer::Impl::Impl(std::string_view serviceName,
+                                      std::string_view serviceType, int port,
+                                      std::span<const std::pair<T, T>> txt) {
+  if (!dynamicDns.CanDnsAnnounce) {
+    return;
+  }
+
+  this->port = port;
+
+  wpi::SmallVector<wchar_t, 128> wideStorage;
+  std::string hostName = wpi::GetHostname() + ".local";
+
+  for (auto&& i : txt) {
+    wideStorage.clear();
+    wpi::sys::windows::UTF8ToUTF16(i.first, wideStorage);
+    this->keys.emplace_back(
+        std::wstring{wideStorage.data(), wideStorage.size()});
+    wideStorage.clear();
+    wpi::sys::windows::UTF8ToUTF16(i.second, wideStorage);
+    this->values.emplace_back(
+        std::wstring{wideStorage.data(), wideStorage.size()});
+  }
+
+  for (size_t i = 0; i < this->keys.size(); i++) {
+    this->keyPtrs.emplace_back(this->keys[i].c_str());
+    this->valuePtrs.emplace_back(this->values[i].c_str());
+  }
+
+  wpi::SmallString<128> storage;
+
+  wideStorage.clear();
+  wpi::sys::windows::UTF8ToUTF16(hostName, wideStorage);
+
+  this->hostName = std::wstring{wideStorage.data(), wideStorage.size()};
+
+  wideStorage.clear();
+  if (wpi::ends_with_lower(serviceType, ".local")) {
+    wpi::sys::windows::UTF8ToUTF16(serviceType, wideStorage);
+  } else {
+    storage.clear();
+    storage.append(serviceType);
+    storage.append(".local");
+    wpi::sys::windows::UTF8ToUTF16(storage.str(), wideStorage);
+  }
+  this->serviceType = std::wstring{wideStorage.data(), wideStorage.size()};
+
+  wideStorage.clear();
+  storage.clear();
+  storage.append(serviceName);
+  storage.append(".");
+  storage.append(serviceType);
+  if (!wpi::ends_with_lower(serviceType, ".local")) {
+    storage.append(".local");
+  }
+
+  wpi::sys::windows::UTF8ToUTF16(storage.str(), wideStorage);
+  this->serviceInstanceName =
+      std::wstring{wideStorage.data(), wideStorage.size()};
+}
+
+MulticastServiceAnnouncer::MulticastServiceAnnouncer(
+    std::string_view serviceName, std::string_view serviceType, int port) {
+  std::span<const std::pair<std::string_view, std::string_view>> txt;
+  pImpl = std::make_unique<Impl>(serviceName, serviceType, port, txt);
+}
+
+MulticastServiceAnnouncer::MulticastServiceAnnouncer(
+    std::string_view serviceName, std::string_view serviceType, int port,
+    std::span<const std::pair<std::string, std::string>> txt) {
+  pImpl = std::make_unique<Impl>(serviceName, serviceType, port, txt);
+}
+
+MulticastServiceAnnouncer::MulticastServiceAnnouncer(
+    std::string_view serviceName, std::string_view serviceType, int port,
+    std::span<const std::pair<std::string_view, std::string_view>> txt) {
+  pImpl = std::make_unique<Impl>(serviceName, serviceType, port, txt);
+}
+
+MulticastServiceAnnouncer::~MulticastServiceAnnouncer() noexcept {
+  Stop();
+}
+
+bool MulticastServiceAnnouncer::HasImplementation() const {
+  return pImpl->dynamicDns.CanDnsAnnounce;
+}
+
+static void WINAPI DnsServiceRegisterCallback(DWORD /*Status*/,
+                                              PVOID pQueryContext,
+                                              PDNS_SERVICE_INSTANCE pInstance) {
+  ImplBase* impl = reinterpret_cast<ImplBase*>(pQueryContext);
+
+  impl->serviceInstance = pInstance;
+
+  SetEvent(impl->event);
+}
+
+void MulticastServiceAnnouncer::Start() {
+  if (pImpl->serviceInstance) {
+    return;
+  }
+
+  if (!pImpl->dynamicDns.CanDnsAnnounce) {
+    return;
+  }
+
+  PDNS_SERVICE_INSTANCE serviceInst =
+      pImpl->dynamicDns.DnsServiceConstructInstancePtr(
+          pImpl->serviceInstanceName.c_str(), pImpl->hostName.c_str(), nullptr,
+          nullptr, pImpl->port, 0, 0, static_cast<DWORD>(pImpl->keyPtrs.size()),
+          pImpl->keyPtrs.data(), pImpl->valuePtrs.data());
+  if (serviceInst == nullptr) {
+    return;
+  }
+
+  DNS_SERVICE_REGISTER_REQUEST registerRequest = {};
+  registerRequest.pQueryContext = static_cast<ImplBase*>(pImpl.get());
+  registerRequest.pRegisterCompletionCallback = DnsServiceRegisterCallback;
+  registerRequest.Version = DNS_QUERY_REQUEST_VERSION1;
+  registerRequest.unicastEnabled = false;
+  registerRequest.pServiceInstance = serviceInst;
+  registerRequest.InterfaceIndex = 0;
+
+  pImpl->event = CreateEvent(NULL, true, false, NULL);
+
+  if (pImpl->dynamicDns.DnsServiceRegisterPtr(&registerRequest, nullptr) ==
+      DNS_REQUEST_PENDING) {
+    WaitForSingleObject(pImpl->event, INFINITE);
+  }
+
+  pImpl->dynamicDns.DnsServiceFreeInstancePtr(serviceInst);
+  CloseHandle(pImpl->event);
+  pImpl->event = nullptr;
+}
+
+static void WINAPI DnsServiceDeRegisterCallback(
+    DWORD /*Status*/, PVOID pQueryContext, PDNS_SERVICE_INSTANCE pInstance) {
+  ImplBase* impl = reinterpret_cast<ImplBase*>(pQueryContext);
+
+  if (pInstance != nullptr) {
+    impl->dynamicDns.DnsServiceFreeInstancePtr(pInstance);
+    pInstance = nullptr;
+  }
+
+  SetEvent(impl->event);
+}
+
+void MulticastServiceAnnouncer::Stop() {
+  if (!pImpl->dynamicDns.CanDnsAnnounce) {
+    return;
+  }
+
+  if (pImpl->serviceInstance == nullptr) {
+    return;
+  }
+
+  pImpl->event = CreateEvent(NULL, true, false, NULL);
+  DNS_SERVICE_REGISTER_REQUEST registerRequest = {};
+  registerRequest.pQueryContext = static_cast<ImplBase*>(pImpl.get());
+  registerRequest.pRegisterCompletionCallback = DnsServiceDeRegisterCallback;
+  registerRequest.Version = DNS_QUERY_REQUEST_VERSION1;
+  registerRequest.unicastEnabled = false;
+  registerRequest.pServiceInstance = pImpl->serviceInstance;
+  registerRequest.InterfaceIndex = 0;
+
+  if (pImpl->dynamicDns.DnsServiceDeRegisterPtr(&registerRequest, nullptr) ==
+      DNS_REQUEST_PENDING) {
+    WaitForSingleObject(pImpl->event, INFINITE);
+  }
+
+  pImpl->dynamicDns.DnsServiceFreeInstancePtr(pImpl->serviceInstance);
+  pImpl->serviceInstance = nullptr;
+  CloseHandle(pImpl->event);
+  pImpl->event = nullptr;
+}
diff --git a/wpinet/src/main/native/windows/MulticastServiceResolver.cpp b/wpinet/src/main/native/windows/MulticastServiceResolver.cpp
new file mode 100644
index 0000000..79fc7b5
--- /dev/null
+++ b/wpinet/src/main/native/windows/MulticastServiceResolver.cpp
@@ -0,0 +1,207 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#ifndef UNICODE
+#define UNICODE
+#endif
+
+#include "wpinet/MulticastServiceResolver.h"
+
+#include <string>
+
+#include <wpi/ConvertUTF.h>
+#include <wpi/SmallString.h>
+#include <wpi/SmallVector.h>
+#include <wpi/StringExtras.h>
+
+#include "DynamicDns.h"
+
+#pragma comment(lib, "dnsapi")
+
+using namespace wpi;
+
+struct MulticastServiceResolver::Impl {
+  wpi::DynamicDns& dynamicDns = wpi::DynamicDns::GetDynamicDns();
+  std::wstring serviceType;
+  DNS_SERVICE_CANCEL serviceCancel{nullptr};
+
+  MulticastServiceResolver* resolver;
+
+  void onFound(ServiceData&& data) {
+    resolver->PushData(std::forward<ServiceData>(data));
+  }
+};
+
+MulticastServiceResolver::MulticastServiceResolver(
+    std::string_view serviceType) {
+  pImpl = std::make_unique<Impl>();
+  pImpl->resolver = this;
+
+  if (!pImpl->dynamicDns.CanDnsResolve) {
+    return;
+  }
+
+  wpi::SmallVector<wchar_t, 128> wideStorage;
+
+  if (wpi::ends_with_lower(serviceType, ".local")) {
+    wpi::sys::windows::UTF8ToUTF16(serviceType, wideStorage);
+  } else {
+    wpi::SmallString<128> storage;
+    storage.append(serviceType);
+    storage.append(".local");
+    wpi::sys::windows::UTF8ToUTF16(storage.str(), wideStorage);
+  }
+  pImpl->serviceType = std::wstring{wideStorage.data(), wideStorage.size()};
+}
+
+MulticastServiceResolver::~MulticastServiceResolver() noexcept {
+  Stop();
+}
+
+bool MulticastServiceResolver::HasImplementation() const {
+  return pImpl->dynamicDns.CanDnsResolve;
+}
+
+static _Function_class_(DNS_QUERY_COMPLETION_ROUTINE) VOID WINAPI
+    DnsCompletion(_In_ PVOID pQueryContext,
+                  _Inout_ PDNS_QUERY_RESULT pQueryResults) {
+  MulticastServiceResolver::Impl* impl =
+      reinterpret_cast<MulticastServiceResolver::Impl*>(pQueryContext);
+
+  wpi::SmallVector<DNS_RECORDW*, 4> PtrRecords;
+  wpi::SmallVector<DNS_RECORDW*, 4> SrvRecords;
+  wpi::SmallVector<DNS_RECORDW*, 4> TxtRecords;
+  wpi::SmallVector<DNS_RECORDW*, 4> ARecords;
+
+  {
+    DNS_RECORDW* current = pQueryResults->pQueryRecords;
+    while (current != nullptr) {
+      switch (current->wType) {
+        case DNS_TYPE_PTR:
+          PtrRecords.push_back(current);
+          break;
+        case DNS_TYPE_SRV:
+          SrvRecords.push_back(current);
+          break;
+        case DNS_TYPE_TEXT:
+          TxtRecords.push_back(current);
+          break;
+        case DNS_TYPE_A:
+          ARecords.push_back(current);
+          break;
+      }
+      current = current->pNext;
+    }
+  }
+
+  for (DNS_RECORDW* Ptr : PtrRecords) {
+    if (std::wstring_view{Ptr->pName} != impl->serviceType) {
+      continue;
+    }
+
+    std::wstring_view nameHost = Ptr->Data.Ptr.pNameHost;
+    DNS_RECORDW* foundSrv = nullptr;
+    for (DNS_RECORDW* Srv : SrvRecords) {
+      if (std::wstring_view{Srv->pName} == nameHost) {
+        foundSrv = Srv;
+        break;
+      }
+    }
+
+    if (!foundSrv) {
+      continue;
+    }
+
+    for (DNS_RECORDW* A : ARecords) {
+      if (std::wstring_view{A->pName} ==
+          std::wstring_view{foundSrv->Data.Srv.pNameTarget}) {
+        MulticastServiceResolver::ServiceData data;
+        wpi::SmallString<128> storage;
+        for (DNS_RECORDW* Txt : TxtRecords) {
+          if (std::wstring_view{Txt->pName} == nameHost) {
+            for (DWORD i = 0; i < Txt->Data.Txt.dwStringCount; i++) {
+              std::wstring_view wideView = Txt->Data.TXT.pStringArray[i];
+              size_t splitIndex = wideView.find(L'=');
+              if (splitIndex == wideView.npos) {
+                // Todo make this just do key
+                continue;
+              }
+              storage.clear();
+              std::span<const wpi::UTF16> wideStr{
+                  reinterpret_cast<const wpi::UTF16*>(wideView.data()),
+                  splitIndex};
+              wpi::convertUTF16ToUTF8String(wideStr, storage);
+              auto& pair =
+                  data.txt.emplace_back(std::pair<std::string, std::string>{
+                      std::string{storage}, {}});
+              storage.clear();
+              wideStr = std::span<const wpi::UTF16>{
+                  reinterpret_cast<const wpi::UTF16*>(wideView.data() +
+                                                      splitIndex + 1),
+                  wideView.size() - splitIndex - 1};
+              wpi::convertUTF16ToUTF8String(wideStr, storage);
+              pair.second = std::string{storage};
+            }
+          }
+        }
+
+        storage.clear();
+        std::span<const wpi::UTF16> wideHostName{
+            reinterpret_cast<const wpi::UTF16*>(A->pName), wcslen(A->pName)};
+        wpi::convertUTF16ToUTF8String(wideHostName, storage);
+        storage.append(".");
+
+        data.hostName = std::string{storage};
+        storage.clear();
+
+        int len = nameHost.find(impl->serviceType.c_str());
+        std::span<const wpi::UTF16> wideServiceName{
+            reinterpret_cast<const wpi::UTF16*>(nameHost.data()),
+            nameHost.size()};
+        if (len != nameHost.npos) {
+          wideServiceName = wideServiceName.subspan(0, len - 1);
+        }
+        wpi::convertUTF16ToUTF8String(wideServiceName, storage);
+
+        data.serviceName = std::string{storage};
+        data.port = foundSrv->Data.Srv.wPort;
+        data.ipv4Address = A->Data.A.IpAddress;
+
+        impl->onFound(std::move(data));
+      }
+    }
+  }
+  DnsFree(pQueryResults->pQueryRecords, DNS_FREE_TYPE::DnsFreeRecordList);
+}
+
+void MulticastServiceResolver::Start() {
+  if (pImpl->serviceCancel.reserved != nullptr) {
+    return;
+  }
+
+  if (!pImpl->dynamicDns.CanDnsResolve) {
+    return;
+  }
+
+  DNS_SERVICE_BROWSE_REQUEST request = {};
+  request.InterfaceIndex = 0;
+  request.pQueryContext = pImpl.get();
+  request.QueryName = pImpl->serviceType.c_str();
+  request.Version = 2;
+  request.pBrowseCallbackV2 = DnsCompletion;
+  pImpl->dynamicDns.DnsServiceBrowsePtr(&request, &pImpl->serviceCancel);
+}
+
+void MulticastServiceResolver::Stop() {
+  if (!pImpl->dynamicDns.CanDnsResolve) {
+    return;
+  }
+
+  if (pImpl->serviceCancel.reserved == nullptr) {
+    return;
+  }
+
+  pImpl->dynamicDns.DnsServiceBrowseCancelPtr(&pImpl->serviceCancel);
+  pImpl->serviceCancel.reserved = nullptr;
+}
diff --git a/wpinet/src/netconsoleServer/native/cpp/main.cpp b/wpinet/src/netconsoleServer/native/cpp/main.cpp
new file mode 100644
index 0000000..0271e36
--- /dev/null
+++ b/wpinet/src/netconsoleServer/native/cpp/main.cpp
@@ -0,0 +1,280 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#ifdef __APPLE__
+#include <util.h>
+#elif !defined(_WIN32)
+#include <pty.h>
+#endif
+
+#include <cstdio>
+
+#include <fmt/format.h>
+#include <wpi/MathExtras.h>
+#include <wpi/SmallVector.h>
+#include <wpi/StringExtras.h>
+#include <wpi/timestamp.h>
+
+#include "wpinet/raw_uv_ostream.h"
+#include "wpinet/uv/Loop.h"
+#include "wpinet/uv/Pipe.h"
+#include "wpinet/uv/Process.h"
+#include "wpinet/uv/Signal.h"
+#include "wpinet/uv/Tcp.h"
+#include "wpinet/uv/Tty.h"
+#include "wpinet/uv/Udp.h"
+#include "wpinet/uv/util.h"
+
+namespace uv = wpi::uv;
+
+static uint64_t startTime = wpi::Now();
+
+static bool NewlineBuffer(std::string& rem, uv::Buffer& buf, size_t len,
+                          wpi::SmallVectorImpl<uv::Buffer>& bufs, bool tcp,
+                          uint16_t tcpSeq) {
+  // scan for last newline
+  std::string_view str(buf.base, len);
+  size_t idx = str.rfind('\n');
+  if (idx == std::string_view::npos) {
+    // no newline yet, just keep appending to remainder
+    rem += str;
+    return false;
+  }
+
+  // build output
+  wpi::raw_uv_ostream out(bufs, 4096);
+  std::string_view toCopy = wpi::slice(str, 0, idx + 1);
+  if (tcp) {
+    // Header is 2 byte len, 1 byte type, 4 byte timestamp, 2 byte sequence num
+    uint32_t ts = wpi::FloatToBits((wpi::Now() - startTime) * 1.0e-6);
+    uint16_t len = rem.size() + toCopy.size() + 1 + 4 + 2;
+    const uint8_t header[] = {static_cast<uint8_t>((len >> 8) & 0xff),
+                              static_cast<uint8_t>(len & 0xff),
+                              12,
+                              static_cast<uint8_t>((ts >> 24) & 0xff),
+                              static_cast<uint8_t>((ts >> 16) & 0xff),
+                              static_cast<uint8_t>((ts >> 8) & 0xff),
+                              static_cast<uint8_t>(ts & 0xff),
+                              static_cast<uint8_t>((tcpSeq >> 8) & 0xff),
+                              static_cast<uint8_t>(tcpSeq & 0xff)};
+    out << std::span<const uint8_t>(header);
+  }
+  out << rem << toCopy;
+
+  // reset remainder
+  rem = wpi::slice(str, idx + 1, std::string_view::npos);
+  return true;
+}
+
+static void CopyUdp(uv::Stream& in, std::shared_ptr<uv::Udp> out,
+                    bool broadcast) {
+  sockaddr_in addr;
+  if (broadcast) {
+    out->SetBroadcast(true);
+    uv::NameToAddr("0.0.0.0", 6666, &addr);
+  } else {
+    uv::NameToAddr("127.0.0.1", 6666, &addr);
+  }
+
+  in.data.connect(
+      [rem = std::make_shared<std::string>(), outPtr = out.get(), addr](
+          uv::Buffer& buf, size_t len) {
+        // build buffers
+        wpi::SmallVector<uv::Buffer, 4> bufs;
+        if (!NewlineBuffer(*rem, buf, len, bufs, false, 0)) {
+          return;
+        }
+
+        // send output
+        outPtr->Send(addr, bufs, [](auto bufs2, uv::Error) {
+          for (auto buf : bufs2) {
+            buf.Deallocate();
+          }
+        });
+      },
+      out);
+}
+
+static void CopyTcp(uv::Stream& in, std::shared_ptr<uv::Stream> out) {
+  struct StreamData {
+    std::string rem;
+    uint16_t seq = 0;
+  };
+  in.data.connect(
+      [data = std::make_shared<StreamData>(), outPtr = out.get()](
+          uv::Buffer& buf, size_t len) {
+        // build buffers
+        wpi::SmallVector<uv::Buffer, 4> bufs;
+        if (!NewlineBuffer(data->rem, buf, len, bufs, true, data->seq++)) {
+          return;
+        }
+
+        // send output
+        outPtr->Write(bufs, [](auto bufs2, uv::Error) {
+          for (auto buf : bufs2) {
+            buf.Deallocate();
+          }
+        });
+      },
+      out);
+}
+
+static void CopyStream(uv::Stream& in, std::shared_ptr<uv::Stream> out) {
+  in.data.connect([out](uv::Buffer& buf, size_t len) {
+    uv::Buffer buf2 = buf.Dup();
+    buf2.len = len;
+    out->Write({buf2}, [](auto bufs, uv::Error) {
+      for (auto buf : bufs) {
+        buf.Deallocate();
+      }
+    });
+  });
+}
+
+int main(int argc, char* argv[]) {
+  // parse arguments
+  int programArgc = 1;
+  bool useUdp = false;
+  bool broadcastUdp = false;
+  bool err = false;
+
+  while (programArgc < argc && argv[programArgc][0] == '-') {
+    if (std::string_view(argv[programArgc]) == "-u") {
+      useUdp = true;
+    } else if (std::string_view(argv[programArgc]) == "-b") {
+      useUdp = true;
+      broadcastUdp = true;
+    } else {
+      fmt::print(stderr, "unrecognized command line option {}\n",
+                 argv[programArgc]);
+      err = true;
+    }
+    ++programArgc;
+  }
+
+  if (err || (argc - programArgc) < 1) {
+    std::fputs(argv[0], stderr);
+    std::fputs(
+        " [-ub] program [arguments ...]\n"
+        "  -u  send udp to localhost port 6666 instead of using tcp\n"
+        "  -b  broadcast udp to port 6666 instead of using tcp\n",
+        stderr);
+    return EXIT_FAILURE;
+  }
+
+  uv::Process::DisableStdioInheritance();
+
+  auto loop = uv::Loop::Create();
+  loop->error.connect(
+      [](uv::Error err) { fmt::print(stderr, "uv ERROR: {}\n", err.str()); });
+
+  // create pipes to communicate with child
+  auto stdinPipe = uv::Pipe::Create(loop);
+  auto stdoutPipe = uv::Pipe::Create(loop);
+  auto stderrPipe = uv::Pipe::Create(loop);
+
+  // create tty to pass from our console to child's
+  auto stdinTty = uv::Tty::Create(loop, 0, true);
+  auto stdoutTty = uv::Tty::Create(loop, 1, false);
+  auto stderrTty = uv::Tty::Create(loop, 2, false);
+
+  // pass through our console to child's (bidirectional)
+  if (stdinTty) {
+    CopyStream(*stdinTty, stdinPipe);
+  }
+  if (stdoutTty) {
+    CopyStream(*stdoutPipe, stdoutTty);
+  }
+  if (stderrTty) {
+    CopyStream(*stderrPipe, stderrTty);
+  }
+
+  // when our stdin closes, also close child stdin
+  if (stdinTty) {
+    stdinTty->end.connect([stdinPipe] { stdinPipe->Close(); });
+  }
+
+  if (useUdp) {
+    auto udp = uv::Udp::Create(loop);
+    // tee stdout and stderr
+    CopyUdp(*stdoutPipe, udp, broadcastUdp);
+    CopyUdp(*stderrPipe, udp, broadcastUdp);
+  } else {
+    auto tcp = uv::Tcp::Create(loop);
+
+    // bind to listen address and port
+    tcp->Bind("", 1740);
+
+    // when we get a connection, accept it
+    tcp->connection.connect([srv = tcp.get(), stdoutPipe, stderrPipe] {
+      auto tcp = srv->Accept();
+      if (!tcp) {
+        return;
+      }
+
+      // close on error
+      tcp->error.connect([s = tcp.get()](wpi::uv::Error err) { s->Close(); });
+
+      // tee stdout and stderr
+      CopyTcp(*stdoutPipe, tcp);
+      CopyTcp(*stderrPipe, tcp);
+    });
+
+    // start listening for incoming connections
+    tcp->Listen();
+  }
+
+  // build process options
+  wpi::SmallVector<uv::Process::Option, 8> options;
+
+  // hook up pipes to child
+  options.emplace_back(
+      uv::Process::StdioCreatePipe(0, *stdinPipe, UV_READABLE_PIPE));
+#ifndef _WIN32
+  // create a PTY so the child does unbuffered output
+  int parentfd, childfd;
+  if (openpty(&parentfd, &childfd, nullptr, nullptr, nullptr) == 0) {
+    stdoutPipe->Open(parentfd);
+    options.emplace_back(uv::Process::StdioInherit(1, childfd));
+  } else {
+    options.emplace_back(
+        uv::Process::StdioCreatePipe(1, *stdoutPipe, UV_WRITABLE_PIPE));
+  }
+#else
+  options.emplace_back(
+      uv::Process::StdioCreatePipe(1, *stdoutPipe, UV_WRITABLE_PIPE));
+#endif
+  options.emplace_back(
+      uv::Process::StdioCreatePipe(2, *stderrPipe, UV_WRITABLE_PIPE));
+
+  // pass our args as the child args (argv[1] becomes child argv[0], etc)
+  for (int i = programArgc; i < argc; ++i) {
+    options.emplace_back(argv[i]);
+  }
+
+  auto proc = uv::Process::SpawnArray(loop, argv[programArgc], options);
+  if (!proc) {
+    std::fputs("could not start subprocess\n", stderr);
+    return EXIT_FAILURE;
+  }
+  proc->exited.connect([](int64_t status, int) { std::exit(status); });
+
+  // start reading
+  if (stdinTty) {
+    stdinTty->StartRead();
+  }
+  stdoutPipe->StartRead();
+  stderrPipe->StartRead();
+
+  // pass various signals to child
+  auto sigHandler = [proc](int signum) { proc->Kill(signum); };
+  for (int signum : {SIGINT, SIGHUP, SIGTERM}) {
+    auto sig = uv::Signal::Create(loop);
+    sig->Start(signum);
+    sig->signal.connect(sigHandler);
+  }
+
+  // run the loop!
+  loop->Run();
+}
diff --git a/wpinet/src/netconsoleTee/native/cpp/main.cpp b/wpinet/src/netconsoleTee/native/cpp/main.cpp
new file mode 100644
index 0000000..a6bdff4
--- /dev/null
+++ b/wpinet/src/netconsoleTee/native/cpp/main.cpp
@@ -0,0 +1,224 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#include <cstdio>
+
+#include <fmt/format.h>
+#include <wpi/MathExtras.h>
+#include <wpi/SmallVector.h>
+#include <wpi/StringExtras.h>
+#include <wpi/timestamp.h>
+
+#include "wpinet/raw_uv_ostream.h"
+#include "wpinet/uv/Loop.h"
+#include "wpinet/uv/Tcp.h"
+#include "wpinet/uv/Tty.h"
+#include "wpinet/uv/Udp.h"
+#include "wpinet/uv/util.h"
+
+namespace uv = wpi::uv;
+
+static uint64_t startTime = wpi::Now();
+
+static bool NewlineBuffer(std::string& rem, uv::Buffer& buf, size_t len,
+                          wpi::SmallVectorImpl<uv::Buffer>& bufs, bool tcp,
+                          uint16_t tcpSeq) {
+  // scan for last newline
+  std::string_view str(buf.base, len);
+  size_t idx = str.rfind('\n');
+  if (idx == std::string_view::npos) {
+    // no newline yet, just keep appending to remainder
+    rem += str;
+    return false;
+  }
+
+  // build output
+  wpi::raw_uv_ostream out(bufs, 4096);
+  std::string_view toCopy = wpi::slice(str, 0, idx + 1);
+  if (tcp) {
+    // Header is 2 byte len, 1 byte type, 4 byte timestamp, 2 byte sequence num
+    uint32_t ts = wpi::FloatToBits((wpi::Now() - startTime) * 1.0e-6);
+    uint16_t len = rem.size() + toCopy.size() + 1 + 4 + 2;
+    const uint8_t header[] = {static_cast<uint8_t>((len >> 8) & 0xff),
+                              static_cast<uint8_t>(len & 0xff),
+                              12,
+                              static_cast<uint8_t>((ts >> 24) & 0xff),
+                              static_cast<uint8_t>((ts >> 16) & 0xff),
+                              static_cast<uint8_t>((ts >> 8) & 0xff),
+                              static_cast<uint8_t>(ts & 0xff),
+                              static_cast<uint8_t>((tcpSeq >> 8) & 0xff),
+                              static_cast<uint8_t>(tcpSeq & 0xff)};
+    out << std::span<const uint8_t>(header);
+  }
+  out << rem << toCopy;
+
+  // reset remainder
+  rem = wpi::slice(str, idx + 1, std::string_view::npos);
+  return true;
+}
+
+static void CopyUdp(uv::Stream& in, std::shared_ptr<uv::Udp> out, int port,
+                    bool broadcast) {
+  sockaddr_in addr;
+  if (broadcast) {
+    out->SetBroadcast(true);
+    uv::NameToAddr("0.0.0.0", port, &addr);
+  } else {
+    uv::NameToAddr("127.0.0.1", port, &addr);
+  }
+
+  in.data.connect(
+      [rem = std::make_shared<std::string>(), outPtr = out.get(), addr](
+          uv::Buffer& buf, size_t len) {
+        // build buffers
+        wpi::SmallVector<uv::Buffer, 4> bufs;
+        if (!NewlineBuffer(*rem, buf, len, bufs, false, 0)) {
+          return;
+        }
+
+        // send output
+        outPtr->Send(addr, bufs, [](auto bufs2, uv::Error) {
+          for (auto buf : bufs2) {
+            buf.Deallocate();
+          }
+        });
+      },
+      out);
+}
+
+static void CopyTcp(uv::Stream& in, std::shared_ptr<uv::Stream> out) {
+  struct StreamData {
+    std::string rem;
+    uint16_t seq = 0;
+  };
+  in.data.connect(
+      [data = std::make_shared<StreamData>(), outPtr = out.get()](
+          uv::Buffer& buf, size_t len) {
+        // build buffers
+        wpi::SmallVector<uv::Buffer, 4> bufs;
+        if (!NewlineBuffer(data->rem, buf, len, bufs, true, data->seq++)) {
+          return;
+        }
+
+        // send output
+        outPtr->Write(bufs, [](auto bufs2, uv::Error) {
+          for (auto buf : bufs2) {
+            buf.Deallocate();
+          }
+        });
+      },
+      out);
+}
+
+static void CopyStream(uv::Stream& in, std::shared_ptr<uv::Stream> out) {
+  in.data.connect([out](uv::Buffer& buf, size_t len) {
+    uv::Buffer buf2 = buf.Dup();
+    buf2.len = len;
+    out->Write({buf2}, [](auto bufs, uv::Error) {
+      for (auto buf : bufs) {
+        buf.Deallocate();
+      }
+    });
+  });
+}
+
+int main(int argc, char* argv[]) {
+  // parse arguments
+  int arg = 1;
+  bool useUdp = false;
+  bool broadcastUdp = false;
+  bool err = false;
+  int port = -1;
+
+  while (arg < argc && argv[arg][0] == '-') {
+    if (std::string_view(argv[arg]) == "-u") {
+      useUdp = true;
+    } else if (std::string_view(argv[arg]) == "-b") {
+      useUdp = true;
+      broadcastUdp = true;
+    } else if (std::string_view(argv[arg]) == "-p") {
+      ++arg;
+      std::optional<int> portValue;
+      if (arg >= argc || argv[arg][0] == '-' ||
+          !(portValue = wpi::parse_integer<int>(argv[arg], 10))) {
+        std::fputs("-p must be followed by port number\n", stderr);
+        err = true;
+      } else if (portValue) {
+        port = portValue.value();
+      }
+    } else {
+      fmt::print(stderr, "unrecognized command line option {}\n", argv[arg]);
+      err = true;
+    }
+    ++arg;
+  }
+
+  if (err) {
+    std::fputs(argv[0], stderr);
+    std::fputs(
+        " [-ub] [-p PORT]\n"
+        "  -u       send udp to localhost port 6666 instead of using tcp\n"
+        "  -b       broadcast udp to port 6666 instead of using tcp\n"
+        "  -p PORT  use port PORT instead of 6666 (udp) or 1740 (tcp)\n",
+        stderr);
+    return EXIT_FAILURE;
+  }
+
+  auto loop = uv::Loop::Create();
+  loop->error.connect(
+      [](uv::Error err) { fmt::print(stderr, "uv ERROR: {}\n", err.str()); });
+
+  // create ttys
+  auto stdinTty = uv::Tty::Create(loop, 0, true);
+  auto stdoutTty = uv::Tty::Create(loop, 1, false);
+
+  // don't bother continuing if we don't have a stdin
+  if (!stdinTty) {
+    return EXIT_SUCCESS;
+  }
+
+  // pass through our input to output
+  if (stdoutTty) {
+    CopyStream(*stdinTty, stdoutTty);
+  }
+
+  // when our stdin closes, exit
+  stdinTty->end.connect([] { std::exit(EXIT_SUCCESS); });
+
+  if (useUdp) {
+    auto udp = uv::Udp::Create(loop);
+    // tee
+    CopyUdp(*stdinTty, udp, port < 0 ? 6666 : port, broadcastUdp);
+  } else {
+    auto tcp = uv::Tcp::Create(loop);
+
+    // bind to listen address and port
+    tcp->Bind("", port < 0 ? 1740 : port);
+
+    // when we get a connection, accept it
+    tcp->connection.connect([srv = tcp.get(), stdinTty] {
+      auto tcp = srv->Accept();
+      if (!tcp) {
+        return;
+      }
+
+      // close on error
+      tcp->error.connect([s = tcp.get()](wpi::uv::Error err) { s->Close(); });
+
+      // tee
+      CopyTcp(*stdinTty, tcp);
+    });
+
+    // start listening for incoming connections
+    tcp->Listen();
+  }
+
+  // start reading
+  if (stdinTty) {
+    stdinTty->StartRead();
+  }
+
+  // run the loop!
+  loop->Run();
+}
diff --git a/wpinet/src/test/java/edu/wpi/first/net/WPINetJNITest.java b/wpinet/src/test/java/edu/wpi/first/net/WPINetJNITest.java
new file mode 100644
index 0000000..dfa6d4d
--- /dev/null
+++ b/wpinet/src/test/java/edu/wpi/first/net/WPINetJNITest.java
@@ -0,0 +1,15 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+package edu.wpi.first.net;
+
+import java.io.IOException;
+import org.junit.jupiter.api.Test;
+
+public class WPINetJNITest {
+  @Test
+  void jniLinkTest() throws IOException {
+    WPINetJNI.forceLoad();
+  }
+}
diff --git a/wpinet/src/test/native/cpp/HttpParserTest.cpp b/wpinet/src/test/native/cpp/HttpParserTest.cpp
new file mode 100644
index 0000000..a9d927a
--- /dev/null
+++ b/wpinet/src/test/native/cpp/HttpParserTest.cpp
@@ -0,0 +1,206 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#include "wpinet/HttpParser.h"  // NOLINT(build/include_order)
+
+#include "gtest/gtest.h"
+
+namespace wpi {
+
+TEST(HttpParserTest, UrlMethodHeadersComplete) {
+  HttpParser p{HttpParser::kRequest};
+  int callbacks = 0;
+  p.url.connect([&](std::string_view path) {
+    ASSERT_EQ(path, "/foo/bar");
+    ASSERT_EQ(p.GetUrl(), "/foo/bar");
+    ++callbacks;
+  });
+  p.Execute("GET /foo");
+  p.Execute("/bar");
+  ASSERT_EQ(callbacks, 0);
+  p.Execute(" HTTP/1.1\r\n\r\n");
+  ASSERT_EQ(callbacks, 1);
+  ASSERT_EQ(p.GetUrl(), "/foo/bar");
+  ASSERT_EQ(p.GetMethod(), HTTP_GET);
+  ASSERT_FALSE(p.HasError());
+}
+
+TEST(HttpParserTest, UrlMethodHeader) {
+  HttpParser p{HttpParser::kRequest};
+  int callbacks = 0;
+  p.url.connect([&](std::string_view path) {
+    ASSERT_EQ(path, "/foo/bar");
+    ASSERT_EQ(p.GetUrl(), "/foo/bar");
+    ++callbacks;
+  });
+  p.Execute("GET /foo");
+  p.Execute("/bar");
+  ASSERT_EQ(callbacks, 0);
+  p.Execute(" HTTP/1.1\r\n");
+  ASSERT_EQ(callbacks, 0);
+  p.Execute("F");
+  ASSERT_EQ(callbacks, 1);
+  ASSERT_EQ(p.GetUrl(), "/foo/bar");
+  ASSERT_EQ(p.GetMethod(), HTTP_GET);
+  ASSERT_FALSE(p.HasError());
+}
+
+TEST(HttpParserTest, StatusHeadersComplete) {
+  HttpParser p{HttpParser::kResponse};
+  int callbacks = 0;
+  p.status.connect([&](std::string_view status) {
+    ASSERT_EQ(status, "OK");
+    ASSERT_EQ(p.GetStatusCode(), 200u);
+    ++callbacks;
+  });
+  p.Execute("HTTP/1.1 200");
+  p.Execute(" OK");
+  ASSERT_EQ(callbacks, 0);
+  p.Execute("\r\n\r\n");
+  ASSERT_EQ(callbacks, 1);
+  ASSERT_EQ(p.GetStatusCode(), 200u);
+  ASSERT_FALSE(p.HasError());
+}
+
+TEST(HttpParserTest, StatusHeader) {
+  HttpParser p{HttpParser::kResponse};
+  int callbacks = 0;
+  p.status.connect([&](std::string_view status) {
+    ASSERT_EQ(status, "OK");
+    ASSERT_EQ(p.GetStatusCode(), 200u);
+    ++callbacks;
+  });
+  p.Execute("HTTP/1.1 200");
+  p.Execute(" OK\r\n");
+  ASSERT_EQ(callbacks, 0);
+  p.Execute("F");
+  ASSERT_EQ(callbacks, 1);
+  ASSERT_EQ(p.GetStatusCode(), 200u);
+  ASSERT_FALSE(p.HasError());
+}
+
+TEST(HttpParserTest, HeaderFieldComplete) {
+  HttpParser p{HttpParser::kRequest};
+  int callbacks = 0;
+  p.header.connect([&](std::string_view name, std::string_view value) {
+    ASSERT_EQ(name, "Foo");
+    ASSERT_EQ(value, "Bar");
+    ++callbacks;
+  });
+  p.Execute("GET / HTTP/1.1\r\n");
+  ASSERT_EQ(callbacks, 0);
+  p.Execute("Fo");
+  ASSERT_EQ(callbacks, 0);
+  p.Execute("o: ");
+  ASSERT_EQ(callbacks, 0);
+  p.Execute("Bar");
+  ASSERT_EQ(callbacks, 0);
+  p.Execute("\r\n");
+  ASSERT_EQ(callbacks, 0);
+  p.Execute("\r\n");
+  ASSERT_EQ(callbacks, 1);
+  ASSERT_FALSE(p.HasError());
+}
+
+TEST(HttpParserTest, HeaderFieldNext) {
+  HttpParser p{HttpParser::kRequest};
+  int callbacks = 0;
+  p.header.connect([&](std::string_view name, std::string_view value) {
+    ASSERT_EQ(name, "Foo");
+    ASSERT_EQ(value, "Bar");
+    ++callbacks;
+  });
+  p.Execute("GET / HTTP/1.1\r\n");
+  ASSERT_EQ(callbacks, 0);
+  p.Execute("Fo");
+  ASSERT_EQ(callbacks, 0);
+  p.Execute("o: ");
+  ASSERT_EQ(callbacks, 0);
+  p.Execute("Bar");
+  ASSERT_EQ(callbacks, 0);
+  p.Execute("\r\n");
+  ASSERT_EQ(callbacks, 0);
+  p.Execute("F");
+  ASSERT_EQ(callbacks, 1);
+  ASSERT_FALSE(p.HasError());
+}
+
+TEST(HttpParserTest, HeadersComplete) {
+  HttpParser p{HttpParser::kRequest};
+  int callbacks = 0;
+  p.headersComplete.connect([&](bool keepAlive) {
+    ASSERT_EQ(keepAlive, false);
+    ++callbacks;
+  });
+  p.Execute("GET / HTTP/1.0\r\n");
+  ASSERT_EQ(callbacks, 0);
+  p.Execute("\r\n");
+  ASSERT_EQ(callbacks, 1);
+  ASSERT_FALSE(p.HasError());
+}
+
+TEST(HttpParserTest, HeadersCompleteHTTP11) {
+  HttpParser p{HttpParser::kRequest};
+  int callbacks = 0;
+  p.headersComplete.connect([&](bool keepAlive) {
+    ASSERT_EQ(keepAlive, true);
+    ++callbacks;
+  });
+  p.Execute("GET / HTTP/1.1\r\n");
+  ASSERT_EQ(callbacks, 0);
+  p.Execute("\r\n");
+  ASSERT_EQ(callbacks, 1);
+  ASSERT_FALSE(p.HasError());
+}
+
+TEST(HttpParserTest, HeadersCompleteKeepAlive) {
+  HttpParser p{HttpParser::kRequest};
+  int callbacks = 0;
+  p.headersComplete.connect([&](bool keepAlive) {
+    ASSERT_EQ(keepAlive, true);
+    ++callbacks;
+  });
+  p.Execute("GET / HTTP/1.0\r\n");
+  ASSERT_EQ(callbacks, 0);
+  p.Execute("Connection: Keep-Alive\r\n");
+  ASSERT_EQ(callbacks, 0);
+  p.Execute("\r\n");
+  ASSERT_EQ(callbacks, 1);
+  ASSERT_FALSE(p.HasError());
+}
+
+TEST(HttpParserTest, HeadersCompleteUpgrade) {
+  HttpParser p{HttpParser::kRequest};
+  int callbacks = 0;
+  p.headersComplete.connect([&](bool) {
+    ASSERT_TRUE(p.IsUpgrade());
+    ++callbacks;
+  });
+  p.Execute("GET / HTTP/1.0\r\n");
+  ASSERT_EQ(callbacks, 0);
+  p.Execute("Connection: Upgrade\r\n");
+  p.Execute("Upgrade: websocket\r\n");
+  ASSERT_EQ(callbacks, 0);
+  p.Execute("\r\n");
+  ASSERT_EQ(callbacks, 1);
+  ASSERT_FALSE(p.HasError());
+}
+
+TEST(HttpParserTest, Reset) {
+  HttpParser p{HttpParser::kRequest};
+  int callbacks = 0;
+  p.headersComplete.connect([&](bool) { ++callbacks; });
+  p.Execute("GET / HTTP/1.1\r\n");
+  ASSERT_EQ(callbacks, 0);
+  p.Execute("\r\n");
+  ASSERT_EQ(callbacks, 1);
+  p.Reset(HttpParser::kRequest);
+  p.Execute("GET / HTTP/1.1\r\n");
+  ASSERT_EQ(callbacks, 1);
+  p.Execute("\r\n");
+  ASSERT_EQ(callbacks, 2);
+  ASSERT_FALSE(p.HasError());
+}
+
+}  // namespace wpi
diff --git a/wpinet/src/test/native/cpp/HttpUtilTest.cpp b/wpinet/src/test/native/cpp/HttpUtilTest.cpp
new file mode 100644
index 0000000..4417b7c
--- /dev/null
+++ b/wpinet/src/test/native/cpp/HttpUtilTest.cpp
@@ -0,0 +1,102 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#include "wpinet/HttpUtil.h"  // NOLINT(build/include_order)
+
+#include "gtest/gtest.h"
+
+namespace wpi {
+
+TEST(HttpMultipartScannerTest, ExecuteExact) {
+  HttpMultipartScanner scanner("foo");
+  EXPECT_TRUE(scanner.Execute("abcdefg---\r\n--foo\r\n").empty());
+  EXPECT_TRUE(scanner.IsDone());
+  EXPECT_TRUE(scanner.GetSkipped().empty());
+}
+
+TEST(HttpMultipartScannerTest, ExecutePartial) {
+  HttpMultipartScanner scanner("foo");
+  EXPECT_TRUE(scanner.Execute("abcdefg--").empty());
+  EXPECT_FALSE(scanner.IsDone());
+  EXPECT_TRUE(scanner.Execute("-\r\n").empty());
+  EXPECT_FALSE(scanner.IsDone());
+  EXPECT_TRUE(scanner.Execute("--foo\r").empty());
+  EXPECT_FALSE(scanner.IsDone());
+  EXPECT_TRUE(scanner.Execute("\n").empty());
+  EXPECT_TRUE(scanner.IsDone());
+}
+
+TEST(HttpMultipartScannerTest, ExecuteTrailing) {
+  HttpMultipartScanner scanner("foo");
+  EXPECT_EQ(scanner.Execute("abcdefg---\r\n--foo\r\nxyz"), "xyz");
+}
+
+TEST(HttpMultipartScannerTest, ExecutePadding) {
+  HttpMultipartScanner scanner("foo");
+  EXPECT_EQ(scanner.Execute("abcdefg---\r\n--foo    \r\nxyz"), "xyz");
+  EXPECT_TRUE(scanner.IsDone());
+}
+
+TEST(HttpMultipartScannerTest, SaveSkipped) {
+  HttpMultipartScanner scanner("foo", true);
+  scanner.Execute("abcdefg---\r\n--foo\r\n");
+  EXPECT_EQ(scanner.GetSkipped(), "abcdefg---\r\n--foo\r\n");
+}
+
+TEST(HttpMultipartScannerTest, Reset) {
+  HttpMultipartScanner scanner("foo", true);
+
+  scanner.Execute("abcdefg---\r\n--foo\r\n");
+  EXPECT_TRUE(scanner.IsDone());
+  EXPECT_EQ(scanner.GetSkipped(), "abcdefg---\r\n--foo\r\n");
+
+  scanner.Reset(true);
+  EXPECT_FALSE(scanner.IsDone());
+  scanner.SetBoundary("bar");
+
+  scanner.Execute("--foo\r\n--bar\r\n");
+  EXPECT_TRUE(scanner.IsDone());
+  EXPECT_EQ(scanner.GetSkipped(), "--foo\r\n--bar\r\n");
+}
+
+TEST(HttpMultipartScannerTest, WithoutDashes) {
+  HttpMultipartScanner scanner("foo", true);
+
+  EXPECT_TRUE(scanner.Execute("--\r\nfoo\r\n").empty());
+  EXPECT_TRUE(scanner.IsDone());
+}
+
+TEST(HttpMultipartScannerTest, SeqDashesDashes) {
+  HttpMultipartScanner scanner("foo", true);
+  EXPECT_TRUE(scanner.Execute("\r\n--foo\r\n").empty());
+  EXPECT_TRUE(scanner.IsDone());
+  EXPECT_TRUE(scanner.Execute("\r\n--foo\r\n").empty());
+  EXPECT_TRUE(scanner.IsDone());
+}
+
+TEST(HttpMultipartScannerTest, SeqDashesNoDashes) {
+  HttpMultipartScanner scanner("foo", true);
+  EXPECT_TRUE(scanner.Execute("\r\n--foo\r\n").empty());
+  EXPECT_TRUE(scanner.IsDone());
+  EXPECT_TRUE(scanner.Execute("\r\nfoo\r\n").empty());
+  EXPECT_FALSE(scanner.IsDone());
+}
+
+TEST(HttpMultipartScannerTest, SeqNoDashesDashes) {
+  HttpMultipartScanner scanner("foo", true);
+  EXPECT_TRUE(scanner.Execute("\r\nfoo\r\n").empty());
+  EXPECT_TRUE(scanner.IsDone());
+  EXPECT_TRUE(scanner.Execute("\r\n--foo\r\n").empty());
+  EXPECT_FALSE(scanner.IsDone());
+}
+
+TEST(HttpMultipartScannerTest, SeqNoDashesNoDashes) {
+  HttpMultipartScanner scanner("foo", true);
+  EXPECT_TRUE(scanner.Execute("\r\nfoo\r\n").empty());
+  EXPECT_TRUE(scanner.IsDone());
+  EXPECT_TRUE(scanner.Execute("\r\nfoo\r\n").empty());
+  EXPECT_TRUE(scanner.IsDone());
+}
+
+}  // namespace wpi
diff --git a/wpinet/src/test/native/cpp/HttpWebSocketServerConnectionTest.cpp b/wpinet/src/test/native/cpp/HttpWebSocketServerConnectionTest.cpp
new file mode 100644
index 0000000..9c2d874
--- /dev/null
+++ b/wpinet/src/test/native/cpp/HttpWebSocketServerConnectionTest.cpp
@@ -0,0 +1,25 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#include "wpinet/HttpWebSocketServerConnection.h"  // NOLINT(build/include_order)
+
+#include <gtest/gtest.h>
+
+namespace wpi {
+
+class HttpWebSocketServerConnectionTest
+    : public HttpWebSocketServerConnection<HttpWebSocketServerConnectionTest> {
+ public:
+  HttpWebSocketServerConnectionTest(std::shared_ptr<uv::Stream> stream,
+                                    std::span<const std::string_view> protocols)
+      : HttpWebSocketServerConnection{stream, protocols} {}
+
+  void ProcessRequest() override { ++gotRequest; }
+  void ProcessWsUpgrade() override { ++gotUpgrade; }
+
+  int gotRequest = 0;
+  int gotUpgrade = 0;
+};
+
+}  // namespace wpi
diff --git a/wpinet/src/test/native/cpp/MulticastTest.cpp b/wpinet/src/test/native/cpp/MulticastTest.cpp
new file mode 100644
index 0000000..bbec538
--- /dev/null
+++ b/wpinet/src/test/native/cpp/MulticastTest.cpp
@@ -0,0 +1,52 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#include <wpinet/MulticastServiceAnnouncer.h>
+#include <wpinet/MulticastServiceResolver.h>
+
+#include <array>
+#include <chrono>
+#include <string>
+#include <string_view>
+#include <thread>
+#include <utility>
+
+#include <wpi/timestamp.h>
+
+#include "gtest/gtest.h"
+
+TEST(MulticastServiceAnnouncerTest, EmptyText) {
+  const std::string_view serviceName = "TestServiceNoText";
+  const std::string_view serviceType = "_wpinotxt";
+  const int port = std::rand();
+  wpi::MulticastServiceAnnouncer announcer(serviceName, serviceType, port);
+  wpi::MulticastServiceResolver resolver(serviceType);
+
+  if (announcer.HasImplementation() && resolver.HasImplementation()) {
+    announcer.Start();
+    resolver.Start();
+
+    std::this_thread::sleep_for(std::chrono::seconds(1));
+
+    resolver.Stop();
+    announcer.Stop();
+  }
+}
+
+TEST(MulticastServiceAnnouncerTest, SingleText) {
+  const std::string_view serviceName = "TestServiceSingle";
+  const std::string_view serviceType = "_wpitxt";
+  const int port = std::rand();
+  std::array<std::pair<std::string, std::string>, 1> txt = {
+      std::make_pair("hello", "world")};
+  wpi::MulticastServiceAnnouncer announcer(serviceName, serviceType, port, txt);
+  wpi::MulticastServiceResolver resolver(serviceType);
+
+  if (announcer.HasImplementation() && resolver.HasImplementation()) {
+    announcer.Start();
+    resolver.Start();
+
+    std::this_thread::sleep_for(std::chrono::seconds(1));
+  }
+}
diff --git a/wpinet/src/test/native/cpp/WebSocketClientTest.cpp b/wpinet/src/test/native/cpp/WebSocketClientTest.cpp
new file mode 100644
index 0000000..a603d9c
--- /dev/null
+++ b/wpinet/src/test/native/cpp/WebSocketClientTest.cpp
@@ -0,0 +1,320 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#include "wpinet/WebSocket.h"  // NOLINT(build/include_order)
+
+#include <wpi/Base64.h>
+#include <wpi/SmallString.h>
+#include <wpi/StringExtras.h>
+#include <wpi/sha1.h>
+
+#include "WebSocketTest.h"
+#include "wpinet/HttpParser.h"
+#include "wpinet/raw_uv_ostream.h"
+
+namespace wpi {
+
+class WebSocketClientTest : public WebSocketTest {
+ public:
+  WebSocketClientTest() {
+    // Bare bones server
+    req.header.connect([this](std::string_view name, std::string_view value) {
+      // save key (required for valid response)
+      if (equals_lower(name, "sec-websocket-key")) {
+        clientKey = value;
+      }
+    });
+    req.headersComplete.connect([this](bool) {
+      // send response
+      SmallVector<uv::Buffer, 4> bufs;
+      raw_uv_ostream os{bufs, 4096};
+      os << "HTTP/1.1 101 Switching Protocols\r\n";
+      os << "Upgrade: websocket\r\n";
+      os << "Connection: Upgrade\r\n";
+
+      // accept hash
+      SHA1 hash;
+      hash.Update(clientKey);
+      hash.Update("258EAFA5-E914-47DA-95CA-C5AB0DC85B11");
+      if (mockBadAccept) {
+        hash.Update("1");
+      }
+      SmallString<64> hashBuf;
+      SmallString<64> acceptBuf;
+      os << "Sec-WebSocket-Accept: "
+         << Base64Encode(hash.RawFinal(hashBuf), acceptBuf) << "\r\n";
+
+      if (!mockProtocol.empty()) {
+        os << "Sec-WebSocket-Protocol: " << mockProtocol << "\r\n";
+      }
+
+      os << "\r\n";
+
+      conn->Write(bufs, [](auto bufs, uv::Error) {
+        for (auto& buf : bufs) {
+          buf.Deallocate();
+        }
+      });
+
+      serverHeadersDone = true;
+      if (connected) {
+        connected();
+      }
+    });
+
+    serverPipe->Listen([this] {
+      conn = serverPipe->Accept();
+      conn->StartRead();
+      conn->data.connect([this](uv::Buffer& buf, size_t size) {
+        std::string_view data{buf.base, size};
+        if (!serverHeadersDone) {
+          data = req.Execute(data);
+          if (req.HasError()) {
+            Finish();
+          }
+          ASSERT_EQ(req.GetError(), HPE_OK) << http_errno_name(req.GetError());
+          if (data.empty()) {
+            return;
+          }
+        }
+        wireData.insert(wireData.end(), data.begin(), data.end());
+      });
+      conn->end.connect([this] { Finish(); });
+    });
+  }
+
+  bool mockBadAccept = false;
+  std::vector<uint8_t> wireData;
+  std::shared_ptr<uv::Pipe> conn;
+  HttpParser req{HttpParser::kRequest};
+  SmallString<64> clientKey;
+  std::string mockProtocol;
+  bool serverHeadersDone = false;
+  std::function<void()> connected;
+};
+
+TEST_F(WebSocketClientTest, Open) {
+  int gotOpen = 0;
+
+  clientPipe->Connect(pipeName, [&] {
+    auto ws = WebSocket::CreateClient(*clientPipe, "/test", pipeName);
+    ws->closed.connect([&](uint16_t code, std::string_view reason) {
+      Finish();
+      if (code != 1005 && code != 1006) {
+        FAIL() << "Code: " << code << " Reason: " << reason;
+      }
+    });
+    ws->open.connect([&](std::string_view protocol) {
+      ++gotOpen;
+      Finish();
+      ASSERT_TRUE(protocol.empty());
+    });
+  });
+
+  loop->Run();
+
+  if (HasFatalFailure()) {
+    return;
+  }
+  ASSERT_EQ(gotOpen, 1);
+}
+
+TEST_F(WebSocketClientTest, BadAccept) {
+  int gotClosed = 0;
+
+  mockBadAccept = true;
+
+  clientPipe->Connect(pipeName, [&] {
+    auto ws = WebSocket::CreateClient(*clientPipe, "/test", pipeName);
+    ws->closed.connect([&](uint16_t code, std::string_view msg) {
+      Finish();
+      ++gotClosed;
+      ASSERT_EQ(code, 1002) << "Message: " << msg;
+    });
+    ws->open.connect([&](std::string_view protocol) {
+      Finish();
+      FAIL() << "Got open";
+    });
+  });
+
+  loop->Run();
+
+  if (HasFatalFailure()) {
+    return;
+  }
+  ASSERT_EQ(gotClosed, 1);
+}
+
+TEST_F(WebSocketClientTest, ProtocolGood) {
+  int gotOpen = 0;
+
+  mockProtocol = "myProtocol";
+
+  clientPipe->Connect(pipeName, [&] {
+    auto ws = WebSocket::CreateClient(*clientPipe, "/test", pipeName,
+                                      {"myProtocol", "myProtocol2"});
+    ws->closed.connect([&](uint16_t code, std::string_view msg) {
+      Finish();
+      if (code != 1005 && code != 1006) {
+        FAIL() << "Code: " << code << "Message: " << msg;
+      }
+    });
+    ws->open.connect([&](std::string_view protocol) {
+      ++gotOpen;
+      Finish();
+      ASSERT_EQ(protocol, "myProtocol");
+    });
+  });
+
+  loop->Run();
+
+  if (HasFatalFailure()) {
+    return;
+  }
+  ASSERT_EQ(gotOpen, 1);
+}
+
+TEST_F(WebSocketClientTest, ProtocolRespNotReq) {
+  int gotClosed = 0;
+
+  mockProtocol = "myProtocol";
+
+  clientPipe->Connect(pipeName, [&] {
+    auto ws = WebSocket::CreateClient(*clientPipe, "/test", pipeName);
+    ws->closed.connect([&](uint16_t code, std::string_view msg) {
+      Finish();
+      ++gotClosed;
+      ASSERT_EQ(code, 1003) << "Message: " << msg;
+    });
+    ws->open.connect([&](std::string_view protocol) {
+      Finish();
+      FAIL() << "Got open";
+    });
+  });
+
+  loop->Run();
+
+  if (HasFatalFailure()) {
+    return;
+  }
+  ASSERT_EQ(gotClosed, 1);
+}
+
+TEST_F(WebSocketClientTest, ProtocolReqNotResp) {
+  int gotClosed = 0;
+
+  clientPipe->Connect(pipeName, [&] {
+    auto ws = WebSocket::CreateClient(*clientPipe, "/test", pipeName,
+                                      {{"myProtocol"}});
+    ws->closed.connect([&](uint16_t code, std::string_view msg) {
+      Finish();
+      ++gotClosed;
+      ASSERT_EQ(code, 1002) << "Message: " << msg;
+    });
+    ws->open.connect([&](std::string_view protocol) {
+      Finish();
+      FAIL() << "Got open";
+    });
+  });
+
+  loop->Run();
+
+  if (HasFatalFailure()) {
+    return;
+  }
+  ASSERT_EQ(gotClosed, 1);
+}
+
+//
+// Send and receive data.  Most of these cases are tested in
+// WebSocketServerTest, so only spot check differences like masking.
+//
+
+class WebSocketClientDataTest : public WebSocketClientTest,
+                                public ::testing::WithParamInterface<size_t> {
+ public:
+  WebSocketClientDataTest() {
+    clientPipe->Connect(pipeName, [&] {
+      ws = WebSocket::CreateClient(*clientPipe, "/test", pipeName);
+      if (setupWebSocket) {
+        setupWebSocket();
+      }
+    });
+  }
+
+  std::function<void()> setupWebSocket;
+  std::shared_ptr<WebSocket> ws;
+};
+
+INSTANTIATE_TEST_SUITE_P(WebSocketClientDataTests, WebSocketClientDataTest,
+                         ::testing::Values(0, 1, 125, 126, 65535, 65536));
+
+TEST_P(WebSocketClientDataTest, SendBinary) {
+  int gotCallback = 0;
+  std::vector<uint8_t> data(GetParam(), 0x03u);
+  setupWebSocket = [&] {
+    ws->open.connect([&](std::string_view) {
+      ws->SendBinary({{data}}, [&](auto bufs, uv::Error) {
+        ++gotCallback;
+        ws->Terminate();
+        ASSERT_FALSE(bufs.empty());
+        ASSERT_EQ(bufs[0].base, reinterpret_cast<const char*>(data.data()));
+      });
+    });
+  };
+
+  loop->Run();
+
+  auto expectData = BuildMessage(0x02, true, true, data);
+  AdjustMasking(wireData);
+  ASSERT_EQ(wireData, expectData);
+  ASSERT_EQ(gotCallback, 1);
+}
+
+TEST_P(WebSocketClientDataTest, ReceiveBinary) {
+  int gotCallback = 0;
+  std::vector<uint8_t> data(GetParam(), 0x03u);
+  setupWebSocket = [&] {
+    ws->binary.connect([&](auto inData, bool fin) {
+      ++gotCallback;
+      ws->Terminate();
+      ASSERT_TRUE(fin);
+      std::vector<uint8_t> recvData{inData.begin(), inData.end()};
+      ASSERT_EQ(data, recvData);
+    });
+  };
+  auto message = BuildMessage(0x02, true, false, data);
+  connected = [&] { conn->Write({{message}}, [&](auto bufs, uv::Error) {}); };
+
+  loop->Run();
+
+  ASSERT_EQ(gotCallback, 1);
+}
+
+//
+// The client must close the connection if a masked frame is received.
+//
+
+TEST_P(WebSocketClientDataTest, ReceiveMasked) {
+  int gotCallback = 0;
+  std::vector<uint8_t> data(GetParam(), ' ');
+  setupWebSocket = [&] {
+    ws->text.connect([&](std::string_view, bool) {
+      ws->Terminate();
+      FAIL() << "Should not have gotten masked message";
+    });
+    ws->closed.connect([&](uint16_t code, std::string_view reason) {
+      ++gotCallback;
+      ASSERT_EQ(code, 1002) << "reason: " << reason;
+    });
+  };
+  auto message = BuildMessage(0x01, true, true, data);
+  connected = [&] { conn->Write({{message}}, [&](auto bufs, uv::Error) {}); };
+
+  loop->Run();
+
+  ASSERT_EQ(gotCallback, 1);
+}
+
+}  // namespace wpi
diff --git a/wpinet/src/test/native/cpp/WebSocketIntegrationTest.cpp b/wpinet/src/test/native/cpp/WebSocketIntegrationTest.cpp
new file mode 100644
index 0000000..5f6c8a5
--- /dev/null
+++ b/wpinet/src/test/native/cpp/WebSocketIntegrationTest.cpp
@@ -0,0 +1,150 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#include "wpinet/WebSocketServer.h"  // NOLINT(build/include_order)
+
+#include <wpi/SmallString.h>
+
+#include "WebSocketTest.h"
+#include "wpinet/HttpParser.h"
+
+namespace wpi {
+
+class WebSocketIntegrationTest : public WebSocketTest {};
+
+TEST_F(WebSocketIntegrationTest, Open) {
+  int gotServerOpen = 0;
+  int gotClientOpen = 0;
+
+  serverPipe->Listen([&]() {
+    auto conn = serverPipe->Accept();
+    auto server = WebSocketServer::Create(*conn);
+    server->connected.connect([&](std::string_view url, WebSocket&) {
+      ++gotServerOpen;
+      ASSERT_EQ(url, "/test");
+    });
+  });
+
+  clientPipe->Connect(pipeName, [&] {
+    auto ws = WebSocket::CreateClient(*clientPipe, "/test", pipeName);
+    ws->closed.connect([&](uint16_t code, std::string_view reason) {
+      Finish();
+      if (code != 1005 && code != 1006) {
+        FAIL() << "Code: " << code << " Reason: " << reason;
+      }
+    });
+    ws->open.connect([&, s = ws.get()](std::string_view) {
+      ++gotClientOpen;
+      s->Close();
+    });
+  });
+
+  loop->Run();
+
+  ASSERT_EQ(gotServerOpen, 1);
+  ASSERT_EQ(gotClientOpen, 1);
+}
+
+TEST_F(WebSocketIntegrationTest, Protocol) {
+  int gotServerOpen = 0;
+  int gotClientOpen = 0;
+
+  serverPipe->Listen([&]() {
+    auto conn = serverPipe->Accept();
+    auto server = WebSocketServer::Create(*conn, {"proto1", "proto2"});
+    server->connected.connect([&](std::string_view, WebSocket& ws) {
+      ++gotServerOpen;
+      ASSERT_EQ(ws.GetProtocol(), "proto1");
+    });
+  });
+
+  clientPipe->Connect(pipeName, [&] {
+    auto ws =
+        WebSocket::CreateClient(*clientPipe, "/test", pipeName, {"proto1"});
+    ws->closed.connect([&](uint16_t code, std::string_view reason) {
+      Finish();
+      if (code != 1005 && code != 1006) {
+        FAIL() << "Code: " << code << " Reason: " << reason;
+      }
+    });
+    ws->open.connect([&, s = ws.get()](std::string_view protocol) {
+      ++gotClientOpen;
+      s->Close();
+      ASSERT_EQ(protocol, "proto1");
+    });
+  });
+
+  loop->Run();
+
+  ASSERT_EQ(gotServerOpen, 1);
+  ASSERT_EQ(gotClientOpen, 1);
+}
+
+TEST_F(WebSocketIntegrationTest, ServerSendBinary) {
+  int gotData = 0;
+
+  serverPipe->Listen([&]() {
+    auto conn = serverPipe->Accept();
+    auto server = WebSocketServer::Create(*conn);
+    server->connected.connect([&](std::string_view, WebSocket& ws) {
+      ws.SendBinary({uv::Buffer{"\x03\x04", 2}}, [&](auto, uv::Error) {});
+      ws.Close();
+    });
+  });
+
+  clientPipe->Connect(pipeName, [&] {
+    auto ws = WebSocket::CreateClient(*clientPipe, "/test", pipeName);
+    ws->closed.connect([&](uint16_t code, std::string_view reason) {
+      Finish();
+      if (code != 1005 && code != 1006) {
+        FAIL() << "Code: " << code << " Reason: " << reason;
+      }
+    });
+    ws->binary.connect([&](auto data, bool) {
+      ++gotData;
+      std::vector<uint8_t> recvData{data.begin(), data.end()};
+      std::vector<uint8_t> expectData{0x03, 0x04};
+      ASSERT_EQ(recvData, expectData);
+    });
+  });
+
+  loop->Run();
+
+  ASSERT_EQ(gotData, 1);
+}
+
+TEST_F(WebSocketIntegrationTest, ClientSendText) {
+  int gotData = 0;
+
+  serverPipe->Listen([&]() {
+    auto conn = serverPipe->Accept();
+    auto server = WebSocketServer::Create(*conn);
+    server->connected.connect([&](std::string_view, WebSocket& ws) {
+      ws.text.connect([&](std::string_view data, bool) {
+        ++gotData;
+        ASSERT_EQ(data, "hello");
+      });
+    });
+  });
+
+  clientPipe->Connect(pipeName, [&] {
+    auto ws = WebSocket::CreateClient(*clientPipe, "/test", pipeName);
+    ws->closed.connect([&](uint16_t code, std::string_view reason) {
+      Finish();
+      if (code != 1005 && code != 1006) {
+        FAIL() << "Code: " << code << " Reason: " << reason;
+      }
+    });
+    ws->open.connect([&, s = ws.get()](std::string_view) {
+      s->SendText({{"hello"}}, [&](auto, uv::Error) {});
+      s->Close();
+    });
+  });
+
+  loop->Run();
+
+  ASSERT_EQ(gotData, 1);
+}
+
+}  // namespace wpi
diff --git a/wpinet/src/test/native/cpp/WebSocketServerTest.cpp b/wpinet/src/test/native/cpp/WebSocketServerTest.cpp
new file mode 100644
index 0000000..18dd50b
--- /dev/null
+++ b/wpinet/src/test/native/cpp/WebSocketServerTest.cpp
@@ -0,0 +1,737 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#include "wpinet/WebSocket.h"  // NOLINT(build/include_order)
+
+#include <wpi/Base64.h>
+#include <wpi/SmallString.h>
+#include <wpi/sha1.h>
+
+#include "WebSocketTest.h"
+#include "wpinet/HttpParser.h"
+#include "wpinet/raw_uv_ostream.h"
+
+namespace wpi {
+
+class WebSocketServerTest : public WebSocketTest {
+ public:
+  WebSocketServerTest() {
+    resp.headersComplete.connect([this](bool) { headersDone = true; });
+
+    serverPipe->Listen([this]() {
+      auto conn = serverPipe->Accept();
+      ws = WebSocket::CreateServer(*conn, "foo", "13");
+      if (setupWebSocket) {
+        setupWebSocket();
+      }
+    });
+    clientPipe->Connect(pipeName, [this]() {
+      clientPipe->StartRead();
+      clientPipe->data.connect([this](uv::Buffer& buf, size_t size) {
+        std::string_view data{buf.base, size};
+        if (!headersDone) {
+          data = resp.Execute(data);
+          if (resp.HasError()) {
+            Finish();
+          }
+          ASSERT_EQ(resp.GetError(), HPE_OK)
+              << http_errno_name(resp.GetError());
+          if (data.empty()) {
+            return;
+          }
+        }
+        wireData.insert(wireData.end(), data.begin(), data.end());
+        if (handleData) {
+          handleData(data);
+        }
+      });
+      clientPipe->end.connect([this]() { Finish(); });
+    });
+  }
+
+  std::function<void()> setupWebSocket;
+  std::function<void(std::string_view)> handleData;
+  std::vector<uint8_t> wireData;
+  std::shared_ptr<WebSocket> ws;
+  HttpParser resp{HttpParser::kResponse};
+  bool headersDone = false;
+};
+
+//
+// Terminate closes the endpoint but doesn't send a close frame.
+//
+
+TEST_F(WebSocketServerTest, Terminate) {
+  int gotClosed = 0;
+  setupWebSocket = [&] {
+    ws->open.connect([&](std::string_view) { ws->Terminate(); });
+    ws->closed.connect([&](uint16_t code, std::string_view reason) {
+      ++gotClosed;
+      ASSERT_EQ(code, 1006) << "reason: " << reason;
+    });
+  };
+
+  loop->Run();
+
+  ASSERT_TRUE(wireData.empty());  // terminate doesn't send data
+  ASSERT_EQ(gotClosed, 1);
+}
+
+TEST_F(WebSocketServerTest, TerminateCode) {
+  int gotClosed = 0;
+  setupWebSocket = [&] {
+    ws->open.connect([&](std::string_view) { ws->Terminate(1000); });
+    ws->closed.connect([&](uint16_t code, std::string_view reason) {
+      ++gotClosed;
+      ASSERT_EQ(code, 1000) << "reason: " << reason;
+    });
+  };
+
+  loop->Run();
+
+  ASSERT_TRUE(wireData.empty());  // terminate doesn't send data
+  ASSERT_EQ(gotClosed, 1);
+}
+
+TEST_F(WebSocketServerTest, TerminateReason) {
+  int gotClosed = 0;
+  setupWebSocket = [&] {
+    ws->open.connect([&](std::string_view) { ws->Terminate(1000, "reason"); });
+    ws->closed.connect([&](uint16_t code, std::string_view reason) {
+      ++gotClosed;
+      ASSERT_EQ(code, 1000);
+      ASSERT_EQ(reason, "reason");
+    });
+  };
+
+  loop->Run();
+
+  ASSERT_TRUE(wireData.empty());  // terminate doesn't send data
+  ASSERT_EQ(gotClosed, 1);
+}
+
+//
+// Close() sends a close frame.
+//
+
+TEST_F(WebSocketServerTest, CloseBasic) {
+  int gotClosed = 0;
+  setupWebSocket = [&] {
+    ws->open.connect([&](std::string_view) { ws->Close(); });
+    ws->closed.connect([&](uint16_t code, std::string_view reason) {
+      ++gotClosed;
+      ASSERT_EQ(code, 1005) << "reason: " << reason;
+    });
+  };
+  // need to respond with close for server to finish shutdown
+  auto message = BuildMessage(0x08, true, true, {});
+  handleData = [&](std::string_view) {
+    clientPipe->Write({{message}}, [&](auto bufs, uv::Error) {});
+  };
+
+  loop->Run();
+
+  auto expectData = BuildMessage(0x08, true, false, {});
+  ASSERT_EQ(wireData, expectData);
+  ASSERT_EQ(gotClosed, 1);
+}
+
+TEST_F(WebSocketServerTest, CloseCode) {
+  int gotClosed = 0;
+  setupWebSocket = [&] {
+    ws->open.connect([&](std::string_view) { ws->Close(1000); });
+    ws->closed.connect([&](uint16_t code, std::string_view reason) {
+      ++gotClosed;
+      ASSERT_EQ(code, 1000) << "reason: " << reason;
+    });
+  };
+  // need to respond with close for server to finish shutdown
+  const uint8_t contents[] = {0x03u, 0xe8u};
+  auto message = BuildMessage(0x08, true, true, contents);
+  handleData = [&](std::string_view) {
+    clientPipe->Write({{message}}, [&](auto bufs, uv::Error) {});
+  };
+
+  loop->Run();
+
+  auto expectData = BuildMessage(0x08, true, false, contents);
+  ASSERT_EQ(wireData, expectData);
+  ASSERT_EQ(gotClosed, 1);
+}
+
+TEST_F(WebSocketServerTest, CloseReason) {
+  int gotClosed = 0;
+  setupWebSocket = [&] {
+    ws->open.connect([&](std::string_view) { ws->Close(1000, "hangup"); });
+    ws->closed.connect([&](uint16_t code, std::string_view reason) {
+      ++gotClosed;
+      ASSERT_EQ(code, 1000);
+      ASSERT_EQ(reason, "hangup");
+    });
+  };
+  // need to respond with close for server to finish shutdown
+  const uint8_t contents[] = {0x03u, 0xe8u, 'h', 'a', 'n', 'g', 'u', 'p'};
+  auto message = BuildMessage(0x08, true, true, contents);
+  handleData = [&](std::string_view) {
+    clientPipe->Write({{message}}, [&](auto bufs, uv::Error) {});
+  };
+
+  loop->Run();
+
+  auto expectData = BuildMessage(0x08, true, false, contents);
+  ASSERT_EQ(wireData, expectData);
+  ASSERT_EQ(gotClosed, 1);
+}
+
+//
+// Receiving a close frame results in closure and echoing the close frame.
+//
+
+TEST_F(WebSocketServerTest, ReceiveCloseBasic) {
+  int gotClosed = 0;
+  setupWebSocket = [&] {
+    ws->closed.connect([&](uint16_t code, std::string_view reason) {
+      ++gotClosed;
+      ASSERT_EQ(code, 1005) << "reason: " << reason;
+    });
+  };
+  auto message = BuildMessage(0x08, true, true, {});
+  resp.headersComplete.connect([&](bool) {
+    clientPipe->Write({{message}}, [&](auto bufs, uv::Error) {});
+  });
+
+  loop->Run();
+
+  // the endpoint should echo the message
+  auto expectData = BuildMessage(0x08, true, false, {});
+  ASSERT_EQ(wireData, expectData);
+  ASSERT_EQ(gotClosed, 1);
+}
+
+TEST_F(WebSocketServerTest, ReceiveCloseCode) {
+  int gotClosed = 0;
+  setupWebSocket = [&] {
+    ws->closed.connect([&](uint16_t code, std::string_view reason) {
+      ++gotClosed;
+      ASSERT_EQ(code, 1000) << "reason: " << reason;
+    });
+  };
+  const uint8_t contents[] = {0x03u, 0xe8u};
+  auto message = BuildMessage(0x08, true, true, contents);
+  resp.headersComplete.connect([&](bool) {
+    clientPipe->Write({{message}}, [&](auto bufs, uv::Error) {});
+  });
+
+  loop->Run();
+
+  // the endpoint should echo the message
+  auto expectData = BuildMessage(0x08, true, false, contents);
+  ASSERT_EQ(wireData, expectData);
+  ASSERT_EQ(gotClosed, 1);
+}
+
+TEST_F(WebSocketServerTest, ReceiveCloseReason) {
+  int gotClosed = 0;
+  setupWebSocket = [&] {
+    ws->closed.connect([&](uint16_t code, std::string_view reason) {
+      ++gotClosed;
+      ASSERT_EQ(code, 1000);
+      ASSERT_EQ(reason, "hangup");
+    });
+  };
+  const uint8_t contents[] = {0x03u, 0xe8u, 'h', 'a', 'n', 'g', 'u', 'p'};
+  auto message = BuildMessage(0x08, true, true, contents);
+  resp.headersComplete.connect([&](bool) {
+    clientPipe->Write({{message}}, [&](auto bufs, uv::Error) {});
+  });
+
+  loop->Run();
+
+  // the endpoint should echo the message
+  auto expectData = BuildMessage(0x08, true, false, contents);
+  ASSERT_EQ(wireData, expectData);
+  ASSERT_EQ(gotClosed, 1);
+}
+
+//
+// If an unknown opcode is received, the receiving endpoint MUST _Fail the
+// WebSocket Connection_.
+//
+
+class WebSocketServerBadOpcodeTest
+    : public WebSocketServerTest,
+      public ::testing::WithParamInterface<uint8_t> {};
+
+INSTANTIATE_TEST_SUITE_P(WebSocketServerBadOpcodeTests,
+                         WebSocketServerBadOpcodeTest,
+                         ::testing::Values(3, 4, 5, 6, 7, 0xb, 0xc, 0xd, 0xe,
+                                           0xf));
+
+TEST_P(WebSocketServerBadOpcodeTest, Receive) {
+  int gotCallback = 0;
+  std::vector<uint8_t> data(4, 0x03);
+  setupWebSocket = [&] {
+    ws->closed.connect([&](uint16_t code, std::string_view reason) {
+      ++gotCallback;
+      ASSERT_EQ(code, 1002) << "reason: " << reason;
+    });
+  };
+  auto message = BuildMessage(GetParam(), true, true, data);
+  resp.headersComplete.connect([&](bool) {
+    clientPipe->Write({{message}}, [&](auto bufs, uv::Error) {});
+  });
+
+  loop->Run();
+
+  ASSERT_EQ(gotCallback, 1);
+}
+
+//
+// Control frames themselves MUST NOT be fragmented.
+//
+
+class WebSocketServerControlFrameTest
+    : public WebSocketServerTest,
+      public ::testing::WithParamInterface<uint8_t> {};
+
+INSTANTIATE_TEST_SUITE_P(WebSocketServerControlFrameTests,
+                         WebSocketServerControlFrameTest,
+                         ::testing::Values(0x8, 0x9, 0xa));
+
+TEST_P(WebSocketServerControlFrameTest, ReceiveFragment) {
+  int gotCallback = 0;
+  std::vector<uint8_t> data(4, 0x03);
+  setupWebSocket = [&] {
+    ws->closed.connect([&](uint16_t code, std::string_view reason) {
+      ++gotCallback;
+      ASSERT_EQ(code, 1002) << "reason: " << reason;
+    });
+  };
+  auto message = BuildMessage(GetParam(), false, true, data);
+  resp.headersComplete.connect([&](bool) {
+    clientPipe->Write({{message}}, [&](auto bufs, uv::Error) {});
+  });
+
+  loop->Run();
+
+  ASSERT_EQ(gotCallback, 1);
+}
+
+//
+// A fragmented message consists of a single frame with the FIN bit
+// clear and an opcode other than 0, followed by zero or more frames
+// with the FIN bit clear and the opcode set to 0, and terminated by
+// a single frame with the FIN bit set and an opcode of 0.
+//
+
+// No previous message
+TEST_F(WebSocketServerTest, ReceiveFragmentInvalidNoPrevFrame) {
+  int gotCallback = 0;
+  std::vector<uint8_t> data(4, 0x03);
+  setupWebSocket = [&] {
+    ws->closed.connect([&](uint16_t code, std::string_view reason) {
+      ++gotCallback;
+      ASSERT_EQ(code, 1002) << "reason: " << reason;
+    });
+  };
+  auto message = BuildMessage(0x00, false, true, data);
+  resp.headersComplete.connect([&](bool) {
+    clientPipe->Write({{message}}, [&](auto bufs, uv::Error) {});
+  });
+
+  loop->Run();
+
+  ASSERT_EQ(gotCallback, 1);
+}
+
+// No previous message with FIN=1.
+TEST_F(WebSocketServerTest, ReceiveFragmentInvalidNoPrevFragment) {
+  int gotCallback = 0;
+  std::vector<uint8_t> data(4, 0x03);
+  setupWebSocket = [&] {
+    ws->closed.connect([&](uint16_t code, std::string_view reason) {
+      ++gotCallback;
+      ASSERT_EQ(code, 1002) << "reason: " << reason;
+    });
+  };
+  auto message = BuildMessage(0x01, true, true, {});  // FIN=1
+  auto message2 = BuildMessage(0x00, false, true, data);
+  resp.headersComplete.connect([&](bool) {
+    clientPipe->Write({{message}, {message2}}, [&](auto bufs, uv::Error) {});
+  });
+
+  loop->Run();
+
+  ASSERT_EQ(gotCallback, 1);
+}
+
+// Incomplete fragment
+TEST_F(WebSocketServerTest, ReceiveFragmentInvalidIncomplete) {
+  int gotCallback = 0;
+  setupWebSocket = [&] {
+    ws->closed.connect([&](uint16_t code, std::string_view reason) {
+      ++gotCallback;
+      ASSERT_EQ(code, 1002) << "reason: " << reason;
+    });
+  };
+  auto message = BuildMessage(0x01, false, true, {});
+  auto message2 = BuildMessage(0x00, false, true, {});
+  auto message3 = BuildMessage(0x01, true, true, {});
+  resp.headersComplete.connect([&](bool) {
+    clientPipe->Write({{message}, {message2}, {message3}},
+                      [&](auto bufs, uv::Error) {});
+  });
+
+  loop->Run();
+
+  ASSERT_EQ(gotCallback, 1);
+}
+
+// Normally fragments are combined into a single callback
+TEST_F(WebSocketServerTest, ReceiveFragment) {
+  int gotCallback = 0;
+
+  std::vector<uint8_t> data(4, 0x03);
+  std::vector<uint8_t> data2(4, 0x04);
+  std::vector<uint8_t> data3(4, 0x05);
+  std::vector<uint8_t> combData{data};
+  combData.insert(combData.end(), data2.begin(), data2.end());
+  combData.insert(combData.end(), data3.begin(), data3.end());
+
+  setupWebSocket = [&] {
+    ws->binary.connect([&](auto inData, bool fin) {
+      ++gotCallback;
+      ws->Terminate();
+      ASSERT_TRUE(fin);
+      std::vector<uint8_t> recvData{inData.begin(), inData.end()};
+      ASSERT_EQ(combData, recvData);
+    });
+  };
+
+  auto message = BuildMessage(0x02, false, true, data);
+  auto message2 = BuildMessage(0x00, false, true, data2);
+  auto message3 = BuildMessage(0x00, true, true, data3);
+  resp.headersComplete.connect([&](bool) {
+    clientPipe->Write({{message}, {message2}, {message3}},
+                      [&](auto bufs, uv::Error) {});
+  });
+
+  loop->Run();
+
+  ASSERT_EQ(gotCallback, 1);
+}
+
+// But can be configured for multiple callbacks
+TEST_F(WebSocketServerTest, ReceiveFragmentSeparate) {
+  int gotCallback = 0;
+
+  std::vector<uint8_t> data(4, 0x03);
+  std::vector<uint8_t> data2(4, 0x04);
+  std::vector<uint8_t> data3(4, 0x05);
+  std::vector<uint8_t> combData{data};
+  combData.insert(combData.end(), data2.begin(), data2.end());
+  combData.insert(combData.end(), data3.begin(), data3.end());
+
+  setupWebSocket = [&] {
+    ws->SetCombineFragments(false);
+    ws->binary.connect([&](auto inData, bool fin) {
+      std::vector<uint8_t> recvData{inData.begin(), inData.end()};
+      switch (++gotCallback) {
+        case 1:
+          ASSERT_FALSE(fin);
+          ASSERT_EQ(data, recvData);
+          break;
+        case 2:
+          ASSERT_FALSE(fin);
+          ASSERT_EQ(data2, recvData);
+          break;
+        case 3:
+          ws->Terminate();
+          ASSERT_TRUE(fin);
+          ASSERT_EQ(data3, recvData);
+          break;
+        default:
+          FAIL() << "too many callbacks";
+          break;
+      }
+    });
+  };
+
+  auto message = BuildMessage(0x02, false, true, data);
+  auto message2 = BuildMessage(0x00, false, true, data2);
+  auto message3 = BuildMessage(0x00, true, true, data3);
+  resp.headersComplete.connect([&](bool) {
+    clientPipe->Write({{message}, {message2}, {message3}},
+                      [&](auto bufs, uv::Error) {});
+  });
+
+  loop->Run();
+
+  ASSERT_EQ(gotCallback, 3);
+}
+
+//
+// Maximum message size is limited.
+//
+
+// Single message
+TEST_F(WebSocketServerTest, ReceiveTooLarge) {
+  int gotCallback = 0;
+  std::vector<uint8_t> data(2048, 0x03u);
+  setupWebSocket = [&] {
+    ws->SetMaxMessageSize(1024);
+    ws->binary.connect([&](auto, bool) {
+      ws->Terminate();
+      FAIL() << "Should not have gotten unmasked message";
+    });
+    ws->closed.connect([&](uint16_t code, std::string_view reason) {
+      ++gotCallback;
+      ASSERT_EQ(code, 1009) << "reason: " << reason;
+    });
+  };
+  auto message = BuildMessage(0x01, true, true, data);
+  resp.headersComplete.connect([&](bool) {
+    clientPipe->Write({{message}}, [&](auto bufs, uv::Error) {});
+  });
+
+  loop->Run();
+
+  ASSERT_EQ(gotCallback, 1);
+}
+
+// Applied across fragments if combining
+TEST_F(WebSocketServerTest, ReceiveTooLargeFragmented) {
+  int gotCallback = 0;
+  std::vector<uint8_t> data(768, 0x03u);
+  setupWebSocket = [&] {
+    ws->SetMaxMessageSize(1024);
+    ws->binary.connect([&](auto, bool) {
+      ws->Terminate();
+      FAIL() << "Should not have gotten unmasked message";
+    });
+    ws->closed.connect([&](uint16_t code, std::string_view reason) {
+      ++gotCallback;
+      ASSERT_EQ(code, 1009) << "reason: " << reason;
+    });
+  };
+  auto message = BuildMessage(0x01, false, true, data);
+  auto message2 = BuildMessage(0x00, true, true, data);
+  resp.headersComplete.connect([&](bool) {
+    clientPipe->Write({{message}, {message2}}, [&](auto bufs, uv::Error) {});
+  });
+
+  loop->Run();
+
+  ASSERT_EQ(gotCallback, 1);
+}
+
+//
+// Send and receive data.
+//
+
+class WebSocketServerDataTest : public WebSocketServerTest,
+                                public ::testing::WithParamInterface<size_t> {};
+
+INSTANTIATE_TEST_SUITE_P(WebSocketServerDataTests, WebSocketServerDataTest,
+                         ::testing::Values(0, 1, 125, 126, 65535, 65536));
+
+TEST_P(WebSocketServerDataTest, SendText) {
+  int gotCallback = 0;
+  std::vector<uint8_t> data(GetParam(), ' ');
+  setupWebSocket = [&] {
+    ws->open.connect([&](std::string_view) {
+      ws->SendText({{data}}, [&](auto bufs, uv::Error) {
+        ++gotCallback;
+        ws->Terminate();
+        ASSERT_FALSE(bufs.empty());
+        ASSERT_EQ(bufs[0].base, reinterpret_cast<const char*>(data.data()));
+      });
+    });
+  };
+
+  loop->Run();
+
+  auto expectData = BuildMessage(0x01, true, false, data);
+  ASSERT_EQ(wireData, expectData);
+  ASSERT_EQ(gotCallback, 1);
+}
+
+TEST_P(WebSocketServerDataTest, SendBinary) {
+  int gotCallback = 0;
+  std::vector<uint8_t> data(GetParam(), 0x03u);
+  setupWebSocket = [&] {
+    ws->open.connect([&](std::string_view) {
+      ws->SendBinary({{data}}, [&](auto bufs, uv::Error) {
+        ++gotCallback;
+        ws->Terminate();
+        ASSERT_FALSE(bufs.empty());
+        ASSERT_EQ(bufs[0].base, reinterpret_cast<const char*>(data.data()));
+      });
+    });
+  };
+
+  loop->Run();
+
+  auto expectData = BuildMessage(0x02, true, false, data);
+  ASSERT_EQ(wireData, expectData);
+  ASSERT_EQ(gotCallback, 1);
+}
+
+TEST_P(WebSocketServerDataTest, SendPing) {
+  int gotCallback = 0;
+  std::vector<uint8_t> data(GetParam(), 0x03u);
+  setupWebSocket = [&] {
+    ws->open.connect([&](std::string_view) {
+      ws->SendPing({{data}}, [&](auto bufs, uv::Error) {
+        ++gotCallback;
+        ws->Terminate();
+        ASSERT_FALSE(bufs.empty());
+        ASSERT_EQ(bufs[0].base, reinterpret_cast<const char*>(data.data()));
+      });
+    });
+  };
+
+  loop->Run();
+
+  auto expectData = BuildMessage(0x09, true, false, data);
+  ASSERT_EQ(wireData, expectData);
+  ASSERT_EQ(gotCallback, 1);
+}
+
+TEST_P(WebSocketServerDataTest, SendPong) {
+  int gotCallback = 0;
+  std::vector<uint8_t> data(GetParam(), 0x03u);
+  setupWebSocket = [&] {
+    ws->open.connect([&](std::string_view) {
+      ws->SendPong({{data}}, [&](auto bufs, uv::Error) {
+        ++gotCallback;
+        ws->Terminate();
+        ASSERT_FALSE(bufs.empty());
+        ASSERT_EQ(bufs[0].base, reinterpret_cast<const char*>(data.data()));
+      });
+    });
+  };
+
+  loop->Run();
+
+  auto expectData = BuildMessage(0x0a, true, false, data);
+  ASSERT_EQ(wireData, expectData);
+  ASSERT_EQ(gotCallback, 1);
+}
+
+TEST_P(WebSocketServerDataTest, ReceiveText) {
+  int gotCallback = 0;
+  std::vector<uint8_t> data(GetParam(), ' ');
+  setupWebSocket = [&] {
+    ws->text.connect([&](std::string_view inData, bool fin) {
+      ++gotCallback;
+      ws->Terminate();
+      ASSERT_TRUE(fin);
+      std::vector<uint8_t> recvData;
+      recvData.insert(recvData.end(), inData.begin(), inData.end());
+      ASSERT_EQ(data, recvData);
+    });
+  };
+  auto message = BuildMessage(0x01, true, true, data);
+  resp.headersComplete.connect([&](bool) {
+    clientPipe->Write({{message}}, [&](auto bufs, uv::Error) {});
+  });
+
+  loop->Run();
+
+  ASSERT_EQ(gotCallback, 1);
+}
+
+TEST_P(WebSocketServerDataTest, ReceiveBinary) {
+  int gotCallback = 0;
+  std::vector<uint8_t> data(GetParam(), 0x03u);
+  setupWebSocket = [&] {
+    ws->binary.connect([&](auto inData, bool fin) {
+      ++gotCallback;
+      ws->Terminate();
+      ASSERT_TRUE(fin);
+      std::vector<uint8_t> recvData{inData.begin(), inData.end()};
+      ASSERT_EQ(data, recvData);
+    });
+  };
+  auto message = BuildMessage(0x02, true, true, data);
+  resp.headersComplete.connect([&](bool) {
+    clientPipe->Write({{message}}, [&](auto bufs, uv::Error) {});
+  });
+
+  loop->Run();
+
+  ASSERT_EQ(gotCallback, 1);
+}
+
+TEST_P(WebSocketServerDataTest, ReceivePing) {
+  int gotCallback = 0;
+  std::vector<uint8_t> data(GetParam(), 0x03u);
+  setupWebSocket = [&] {
+    ws->ping.connect([&](auto inData) {
+      ++gotCallback;
+      ws->Terminate();
+      std::vector<uint8_t> recvData{inData.begin(), inData.end()};
+      ASSERT_EQ(data, recvData);
+    });
+  };
+  auto message = BuildMessage(0x09, true, true, data);
+  resp.headersComplete.connect([&](bool) {
+    clientPipe->Write({{message}}, [&](auto bufs, uv::Error) {});
+  });
+
+  loop->Run();
+
+  ASSERT_EQ(gotCallback, 1);
+}
+
+TEST_P(WebSocketServerDataTest, ReceivePong) {
+  int gotCallback = 0;
+  std::vector<uint8_t> data(GetParam(), 0x03u);
+  setupWebSocket = [&] {
+    ws->pong.connect([&](auto inData) {
+      ++gotCallback;
+      ws->Terminate();
+      std::vector<uint8_t> recvData{inData.begin(), inData.end()};
+      ASSERT_EQ(data, recvData);
+    });
+  };
+  auto message = BuildMessage(0x0a, true, true, data);
+  resp.headersComplete.connect([&](bool) {
+    clientPipe->Write({{message}}, [&](auto bufs, uv::Error) {});
+  });
+
+  loop->Run();
+
+  ASSERT_EQ(gotCallback, 1);
+}
+
+//
+// The server must close the connection if an unmasked frame is received.
+//
+
+TEST_P(WebSocketServerDataTest, ReceiveUnmasked) {
+  int gotCallback = 0;
+  std::vector<uint8_t> data(GetParam(), ' ');
+  setupWebSocket = [&] {
+    ws->text.connect([&](std::string_view, bool) {
+      ws->Terminate();
+      FAIL() << "Should not have gotten unmasked message";
+    });
+    ws->closed.connect([&](uint16_t code, std::string_view reason) {
+      ++gotCallback;
+      ASSERT_EQ(code, 1002) << "reason: " << reason;
+    });
+  };
+  auto message = BuildMessage(0x01, true, false, data);
+  resp.headersComplete.connect([&](bool) {
+    clientPipe->Write({{message}}, [&](auto bufs, uv::Error) {});
+  });
+
+  loop->Run();
+
+  ASSERT_EQ(gotCallback, 1);
+}
+
+}  // namespace wpi
diff --git a/wpinet/src/test/native/cpp/WebSocketTest.cpp b/wpinet/src/test/native/cpp/WebSocketTest.cpp
new file mode 100644
index 0000000..e5f03de
--- /dev/null
+++ b/wpinet/src/test/native/cpp/WebSocketTest.cpp
@@ -0,0 +1,379 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#include "wpinet/WebSocket.h"  // NOLINT(build/include_order)
+
+#include "WebSocketTest.h"
+
+#include <wpi/StringExtras.h>
+
+#include "wpinet/HttpParser.h"
+
+namespace wpi {
+
+#ifdef _WIN32
+const char* WebSocketTest::pipeName = "\\\\.\\pipe\\websocket-unit-test";
+#else
+const char* WebSocketTest::pipeName = "/tmp/websocket-unit-test";
+#endif
+const uint8_t WebSocketTest::testMask[4] = {0x11, 0x22, 0x33, 0x44};
+
+void WebSocketTest::SetUpTestCase() {
+#ifndef _WIN32
+  unlink(pipeName);
+#endif
+}
+
+std::vector<uint8_t> WebSocketTest::BuildHeader(uint8_t opcode, bool fin,
+                                                bool masking, uint64_t len) {
+  std::vector<uint8_t> data;
+  data.push_back(opcode | (fin ? 0x80u : 0x00u));
+  if (len < 126) {
+    data.push_back(len | (masking ? 0x80 : 0x00u));
+  } else if (len < 65536) {
+    data.push_back(126u | (masking ? 0x80 : 0x00u));
+    data.push_back(len >> 8);
+    data.push_back(len & 0xff);
+  } else {
+    data.push_back(127u | (masking ? 0x80u : 0x00u));
+    for (int i = 56; i >= 0; i -= 8) {
+      data.push_back((len >> i) & 0xff);
+    }
+  }
+  if (masking) {
+    data.insert(data.end(), &testMask[0], &testMask[4]);
+  }
+  return data;
+}
+
+std::vector<uint8_t> WebSocketTest::BuildMessage(
+    uint8_t opcode, bool fin, bool masking, std::span<const uint8_t> data) {
+  auto finalData = BuildHeader(opcode, fin, masking, data.size());
+  size_t headerSize = finalData.size();
+  finalData.insert(finalData.end(), data.begin(), data.end());
+  if (masking) {
+    uint8_t mask[4] = {finalData[headerSize - 4], finalData[headerSize - 3],
+                       finalData[headerSize - 2], finalData[headerSize - 1]};
+    int n = 0;
+    for (size_t i = headerSize, end = finalData.size(); i < end; ++i) {
+      finalData[i] ^= mask[n++];
+      if (n >= 4) {
+        n = 0;
+      }
+    }
+  }
+  return finalData;
+}
+
+// If the message is masked, changes the mask to match the mask set by
+// BuildHeader() by unmasking and remasking.
+void WebSocketTest::AdjustMasking(std::span<uint8_t> message) {
+  if (message.size() < 2) {
+    return;
+  }
+  if ((message[1] & 0x80) == 0) {
+    return;  // not masked
+  }
+  size_t maskPos;
+  uint8_t len = message[1] & 0x7f;
+  if (len == 126) {
+    maskPos = 4;
+  } else if (len == 127) {
+    maskPos = 10;
+  } else {
+    maskPos = 2;
+  }
+  uint8_t mask[4] = {message[maskPos], message[maskPos + 1],
+                     message[maskPos + 2], message[maskPos + 3]};
+  message[maskPos] = testMask[0];
+  message[maskPos + 1] = testMask[1];
+  message[maskPos + 2] = testMask[2];
+  message[maskPos + 3] = testMask[3];
+  int n = 0;
+  for (auto& ch : message.subspan(maskPos + 4)) {
+    ch ^= mask[n] ^ testMask[n];
+    if (++n >= 4) {
+      n = 0;
+    }
+  }
+}
+
+TEST_F(WebSocketTest, CreateClientBasic) {
+  int gotHost = 0;
+  int gotUpgrade = 0;
+  int gotConnection = 0;
+  int gotKey = 0;
+  int gotVersion = 0;
+
+  HttpParser req{HttpParser::kRequest};
+  req.url.connect([](std::string_view url) { ASSERT_EQ(url, "/test"); });
+  req.header.connect([&](std::string_view name, std::string_view value) {
+    if (equals_lower(name, "host")) {
+      ASSERT_EQ(value, pipeName);
+      ++gotHost;
+    } else if (equals_lower(name, "upgrade")) {
+      ASSERT_EQ(value, "websocket");
+      ++gotUpgrade;
+    } else if (equals_lower(name, "connection")) {
+      ASSERT_EQ(value, "Upgrade");
+      ++gotConnection;
+    } else if (equals_lower(name, "sec-websocket-key")) {
+      ++gotKey;
+    } else if (equals_lower(name, "sec-websocket-version")) {
+      ASSERT_EQ(value, "13");
+      ++gotVersion;
+    } else {
+      FAIL() << "unexpected header " << name;
+    }
+  });
+  req.headersComplete.connect([&](bool) { Finish(); });
+
+  serverPipe->Listen([&]() {
+    auto conn = serverPipe->Accept();
+    conn->StartRead();
+    conn->data.connect([&](uv::Buffer& buf, size_t size) {
+      req.Execute(std::string_view{buf.base, size});
+      if (req.HasError()) {
+        Finish();
+      }
+      ASSERT_EQ(req.GetError(), HPE_OK) << http_errno_name(req.GetError());
+    });
+  });
+  clientPipe->Connect(pipeName, [&]() {
+    auto ws = WebSocket::CreateClient(*clientPipe, "/test", pipeName);
+  });
+
+  loop->Run();
+
+  if (HasFatalFailure()) {
+    return;
+  }
+  ASSERT_EQ(gotHost, 1);
+  ASSERT_EQ(gotUpgrade, 1);
+  ASSERT_EQ(gotConnection, 1);
+  ASSERT_EQ(gotKey, 1);
+  ASSERT_EQ(gotVersion, 1);
+}
+
+TEST_F(WebSocketTest, CreateClientExtraHeaders) {
+  int gotExtra1 = 0;
+  int gotExtra2 = 0;
+  HttpParser req{HttpParser::kRequest};
+  req.header.connect([&](std::string_view name, std::string_view value) {
+    if (equals(name, "Extra1")) {
+      ASSERT_EQ(value, "Data1");
+      ++gotExtra1;
+    } else if (equals(name, "Extra2")) {
+      ASSERT_EQ(value, "Data2");
+      ++gotExtra2;
+    }
+  });
+  req.headersComplete.connect([&](bool) { Finish(); });
+
+  serverPipe->Listen([&]() {
+    auto conn = serverPipe->Accept();
+    conn->StartRead();
+    conn->data.connect([&](uv::Buffer& buf, size_t size) {
+      req.Execute(std::string_view{buf.base, size});
+      if (req.HasError()) {
+        Finish();
+      }
+      ASSERT_EQ(req.GetError(), HPE_OK) << http_errno_name(req.GetError());
+    });
+  });
+  clientPipe->Connect(pipeName, [&]() {
+    WebSocket::ClientOptions options;
+    SmallVector<std::pair<std::string_view, std::string_view>, 4> extraHeaders;
+    extraHeaders.emplace_back("Extra1", "Data1");
+    extraHeaders.emplace_back("Extra2", "Data2");
+    options.extraHeaders = extraHeaders;
+    auto ws =
+        WebSocket::CreateClient(*clientPipe, "/test", pipeName, {}, options);
+  });
+
+  loop->Run();
+
+  if (HasFatalFailure()) {
+    return;
+  }
+  ASSERT_EQ(gotExtra1, 1);
+  ASSERT_EQ(gotExtra2, 1);
+}
+
+TEST_F(WebSocketTest, CreateClientTimeout) {
+  int gotClosed = 0;
+  serverPipe->Listen([&]() { auto conn = serverPipe->Accept(); });
+  clientPipe->Connect(pipeName, [&]() {
+    WebSocket::ClientOptions options;
+    options.handshakeTimeout = uv::Timer::Time{100};
+    auto ws =
+        WebSocket::CreateClient(*clientPipe, "/test", pipeName, {}, options);
+    ws->closed.connect([&](uint16_t code, std::string_view) {
+      Finish();
+      ++gotClosed;
+      ASSERT_EQ(code, 1006);
+    });
+  });
+
+  loop->Run();
+
+  if (HasFatalFailure()) {
+    return;
+  }
+  ASSERT_EQ(gotClosed, 1);
+}
+
+TEST_F(WebSocketTest, CreateServerBasic) {
+  int gotStatus = 0;
+  int gotUpgrade = 0;
+  int gotConnection = 0;
+  int gotAccept = 0;
+  int gotOpen = 0;
+
+  HttpParser resp{HttpParser::kResponse};
+  resp.status.connect([&](std::string_view status) {
+    ++gotStatus;
+    ASSERT_EQ(resp.GetStatusCode(), 101u) << "status: " << status;
+  });
+  resp.header.connect([&](std::string_view name, std::string_view value) {
+    if (equals_lower(name, "upgrade")) {
+      ASSERT_EQ(value, "websocket");
+      ++gotUpgrade;
+    } else if (equals_lower(name, "connection")) {
+      ASSERT_EQ(value, "Upgrade");
+      ++gotConnection;
+    } else if (equals_lower(name, "sec-websocket-accept")) {
+      ASSERT_EQ(value, "s3pPLMBiTxaQ9kYGzzhZRbK+xOo=");
+      ++gotAccept;
+    } else {
+      FAIL() << "unexpected header " << name;
+    }
+  });
+  resp.headersComplete.connect([&](bool) { Finish(); });
+
+  serverPipe->Listen([&]() {
+    auto conn = serverPipe->Accept();
+    auto ws = WebSocket::CreateServer(*conn, "dGhlIHNhbXBsZSBub25jZQ==", "13");
+    ws->open.connect([&](std::string_view protocol) {
+      ++gotOpen;
+      ASSERT_TRUE(protocol.empty());
+    });
+  });
+  clientPipe->Connect(pipeName, [&] {
+    clientPipe->StartRead();
+    clientPipe->data.connect([&](uv::Buffer& buf, size_t size) {
+      resp.Execute(std::string_view{buf.base, size});
+      if (resp.HasError()) {
+        Finish();
+      }
+      ASSERT_EQ(resp.GetError(), HPE_OK) << http_errno_name(resp.GetError());
+    });
+  });
+
+  loop->Run();
+
+  if (HasFatalFailure()) {
+    return;
+  }
+  ASSERT_EQ(gotStatus, 1);
+  ASSERT_EQ(gotUpgrade, 1);
+  ASSERT_EQ(gotConnection, 1);
+  ASSERT_EQ(gotAccept, 1);
+  ASSERT_EQ(gotOpen, 1);
+}
+
+TEST_F(WebSocketTest, CreateServerProtocol) {
+  int gotProtocol = 0;
+  int gotOpen = 0;
+
+  HttpParser resp{HttpParser::kResponse};
+  resp.header.connect([&](std::string_view name, std::string_view value) {
+    if (equals_lower(name, "sec-websocket-protocol")) {
+      ++gotProtocol;
+      ASSERT_EQ(value, "myProtocol");
+    }
+  });
+  resp.headersComplete.connect([&](bool) { Finish(); });
+
+  serverPipe->Listen([&]() {
+    auto conn = serverPipe->Accept();
+    auto ws = WebSocket::CreateServer(*conn, "foo", "13", "myProtocol");
+    ws->open.connect([&](std::string_view protocol) {
+      ++gotOpen;
+      ASSERT_EQ(protocol, "myProtocol");
+    });
+  });
+  clientPipe->Connect(pipeName, [&] {
+    clientPipe->StartRead();
+    clientPipe->data.connect([&](uv::Buffer& buf, size_t size) {
+      resp.Execute(std::string_view{buf.base, size});
+      if (resp.HasError()) {
+        Finish();
+      }
+      ASSERT_EQ(resp.GetError(), HPE_OK) << http_errno_name(resp.GetError());
+    });
+  });
+
+  loop->Run();
+
+  if (HasFatalFailure()) {
+    return;
+  }
+  ASSERT_EQ(gotProtocol, 1);
+  ASSERT_EQ(gotOpen, 1);
+}
+
+TEST_F(WebSocketTest, CreateServerBadVersion) {
+  int gotStatus = 0;
+  int gotVersion = 0;
+  int gotUpgrade = 0;
+
+  HttpParser resp{HttpParser::kResponse};
+  resp.status.connect([&](std::string_view status) {
+    ++gotStatus;
+    ASSERT_EQ(resp.GetStatusCode(), 426u) << "status: " << status;
+  });
+  resp.header.connect([&](std::string_view name, std::string_view value) {
+    if (equals_lower(name, "sec-websocket-version")) {
+      ++gotVersion;
+      ASSERT_EQ(value, "13");
+    } else if (equals_lower(name, "upgrade")) {
+      ++gotUpgrade;
+      ASSERT_EQ(value, "WebSocket");
+    } else {
+      FAIL() << "unexpected header " << name;
+    }
+  });
+  resp.headersComplete.connect([&](bool) { Finish(); });
+
+  serverPipe->Listen([&] {
+    auto conn = serverPipe->Accept();
+    auto ws = WebSocket::CreateServer(*conn, "foo", "14");
+    ws->open.connect([&](std::string_view) {
+      Finish();
+      FAIL();
+    });
+  });
+  clientPipe->Connect(pipeName, [&] {
+    clientPipe->StartRead();
+    clientPipe->data.connect([&](uv::Buffer& buf, size_t size) {
+      resp.Execute(std::string_view{buf.base, size});
+      if (resp.HasError()) {
+        Finish();
+      }
+      ASSERT_EQ(resp.GetError(), HPE_OK) << http_errno_name(resp.GetError());
+    });
+  });
+
+  loop->Run();
+
+  if (HasFatalFailure()) {
+    return;
+  }
+  ASSERT_EQ(gotStatus, 1);
+  ASSERT_EQ(gotVersion, 1);
+  ASSERT_EQ(gotUpgrade, 1);
+}
+
+}  // namespace wpi
diff --git a/wpinet/src/test/native/cpp/WebSocketTest.h b/wpinet/src/test/native/cpp/WebSocketTest.h
new file mode 100644
index 0000000..903ce00
--- /dev/null
+++ b/wpinet/src/test/native/cpp/WebSocketTest.h
@@ -0,0 +1,72 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#pragma once
+
+#include <cstdio>
+#include <memory>
+#include <span>
+#include <vector>
+
+#include "gtest/gtest.h"
+#include "wpinet/uv/Loop.h"
+#include "wpinet/uv/Pipe.h"
+#include "wpinet/uv/Timer.h"
+
+namespace wpi {
+
+class WebSocketTest : public ::testing::Test {
+ public:
+  static const char* pipeName;
+
+  static void SetUpTestCase();
+
+  WebSocketTest() {
+    loop = uv::Loop::Create();
+    clientPipe = uv::Pipe::Create(loop);
+    serverPipe = uv::Pipe::Create(loop);
+
+    serverPipe->Bind(pipeName);
+
+#if 0
+    auto debugTimer = uv::Timer::Create(loop);
+    debugTimer->timeout.connect([this] {
+      std::printf("Active handles:\n");
+      uv_print_active_handles(loop->GetRaw(), stdout);
+    });
+    debugTimer->Start(uv::Timer::Time{100}, uv::Timer::Time{100});
+    debugTimer->Unreference();
+#endif
+
+    auto failTimer = uv::Timer::Create(loop);
+    failTimer->timeout.connect([this] {
+      loop->Stop();
+      FAIL() << "loop failed to terminate";
+    });
+    failTimer->Start(uv::Timer::Time{1000});
+    failTimer->Unreference();
+  }
+
+  ~WebSocketTest() override {
+    Finish();
+  }
+
+  void Finish() {
+    loop->Walk([](uv::Handle& it) { it.Close(); });
+  }
+
+  static std::vector<uint8_t> BuildHeader(uint8_t opcode, bool fin,
+                                          bool masking, uint64_t len);
+  static std::vector<uint8_t> BuildMessage(uint8_t opcode, bool fin,
+                                           bool masking,
+                                           std::span<const uint8_t> data);
+  static void AdjustMasking(std::span<uint8_t> message);
+  static const uint8_t testMask[4];
+
+  std::shared_ptr<uv::Loop> loop;
+  std::shared_ptr<uv::Pipe> clientPipe;
+  std::shared_ptr<uv::Pipe> serverPipe;
+};
+
+}  // namespace wpi
diff --git a/wpinet/src/test/native/cpp/WorkerThreadTest.cpp b/wpinet/src/test/native/cpp/WorkerThreadTest.cpp
new file mode 100644
index 0000000..8279cb1
--- /dev/null
+++ b/wpinet/src/test/native/cpp/WorkerThreadTest.cpp
@@ -0,0 +1,86 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#include "wpinet/WorkerThread.h"  // NOLINT(build/include_order)
+
+#include "gtest/gtest.h"  // NOLINT(build/include_order)
+
+#include <thread>
+
+#include <wpi/condition_variable.h>
+#include <wpi/mutex.h>
+
+#include "wpinet/EventLoopRunner.h"
+#include "wpinet/uv/Loop.h"
+
+namespace wpi {
+
+TEST(WorkerThreadTest, Future) {
+  WorkerThread<int(bool)> worker;
+  future<int> f =
+      worker.QueueWork([](bool v) -> int { return v ? 1 : 2; }, true);
+  ASSERT_EQ(f.get(), 1);
+}
+
+TEST(WorkerThreadTest, FutureVoid) {
+  int callbacks = 0;
+  WorkerThread<void(int)> worker;
+  future<void> f = worker.QueueWork(
+      [&](int v) {
+        ++callbacks;
+        ASSERT_EQ(v, 3);
+      },
+      3);
+  f.get();
+  ASSERT_EQ(callbacks, 1);
+}
+
+TEST(WorkerThreadTest, Loop) {
+  mutex m;
+  condition_variable cv;
+  int callbacks = 0;
+
+  WorkerThread<int(bool)> worker;
+  EventLoopRunner runner;
+  runner.ExecSync([&](uv::Loop& loop) { worker.SetLoop(loop); });
+  worker.QueueWorkThen([](bool v) -> int { return v ? 1 : 2; },
+                       [&](int v2) {
+                         std::scoped_lock lock{m};
+                         ++callbacks;
+                         cv.notify_all();
+                         ASSERT_EQ(v2, 1);
+                       },
+                       true);
+  auto f = worker.QueueWork([&](bool) -> int { return 2; }, true);
+  ASSERT_EQ(f.get(), 2);
+
+  std::unique_lock lock{m};
+  cv.wait(lock, [&] { return callbacks == 1; });
+  ASSERT_EQ(callbacks, 1);
+}
+
+TEST(WorkerThreadTest, LoopVoid) {
+  mutex m;
+  condition_variable cv;
+  int callbacks = 0;
+
+  WorkerThread<void(bool)> worker;
+  EventLoopRunner runner;
+  runner.ExecSync([&](uv::Loop& loop) { worker.SetLoop(loop); });
+  worker.QueueWorkThen([](bool) {},
+                       [&]() {
+                         std::scoped_lock lock{m};
+                         ++callbacks;
+                         cv.notify_all();
+                       },
+                       true);
+  auto f = worker.QueueWork([](bool) {}, true);
+  f.get();
+
+  std::unique_lock lock{m};
+  cv.wait(lock, [&] { return callbacks == 1; });
+  ASSERT_EQ(callbacks, 1);
+}
+
+}  // namespace wpi
diff --git a/wpinet/src/test/native/cpp/hostname.cpp b/wpinet/src/test/native/cpp/hostname.cpp
new file mode 100644
index 0000000..51a29a2
--- /dev/null
+++ b/wpinet/src/test/native/cpp/hostname.cpp
@@ -0,0 +1,24 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#include "wpinet/hostname.h"
+
+#include <wpi/SmallString.h>
+#include <wpi/SmallVector.h>
+
+#include "gtest/gtest.h"
+
+namespace wpi {
+TEST(HostNameTest, HostNameNotEmpty) {
+  ASSERT_NE(GetHostname(), "");
+}
+TEST(HostNameTest, HostNameNotEmptySmallVector) {
+  SmallVector<char, 256> name;
+  ASSERT_NE(GetHostname(name), "");
+}
+TEST(HostNameTest, HostNameEq) {
+  SmallVector<char, 256> nameBuf;
+  ASSERT_EQ(GetHostname(nameBuf), GetHostname());
+}
+}  // namespace wpi
diff --git a/wpinet/src/test/native/cpp/main.cpp b/wpinet/src/test/native/cpp/main.cpp
new file mode 100644
index 0000000..09072ee
--- /dev/null
+++ b/wpinet/src/test/native/cpp/main.cpp
@@ -0,0 +1,11 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#include "gtest/gtest.h"
+
+int main(int argc, char** argv) {
+  ::testing::InitGoogleTest(&argc, argv);
+  int ret = RUN_ALL_TESTS();
+  return ret;
+}
diff --git a/wpinet/src/test/native/cpp/raw_uv_stream_test.cpp b/wpinet/src/test/native/cpp/raw_uv_stream_test.cpp
new file mode 100644
index 0000000..541da5a
--- /dev/null
+++ b/wpinet/src/test/native/cpp/raw_uv_stream_test.cpp
@@ -0,0 +1,71 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#include "wpinet/raw_uv_ostream.h"  // NOLINT(build/include_order)
+
+#include "gtest/gtest.h"
+
+namespace wpi {
+
+TEST(RawUvStreamTest, BasicWrite) {
+  SmallVector<uv::Buffer, 4> bufs;
+  raw_uv_ostream os(bufs, 1024);
+  os << "12";
+  os << "34";
+  ASSERT_EQ(bufs.size(), 1u);
+  ASSERT_EQ(bufs[0].len, 4u);
+  ASSERT_EQ(bufs[0].base[0], '1');
+  ASSERT_EQ(bufs[0].base[1], '2');
+  ASSERT_EQ(bufs[0].base[2], '3');
+  ASSERT_EQ(bufs[0].base[3], '4');
+
+  for (auto& buf : bufs) {
+    buf.Deallocate();
+  }
+}
+
+TEST(RawUvStreamTest, BoundaryWrite) {
+  SmallVector<uv::Buffer, 4> bufs;
+  raw_uv_ostream os(bufs, 4);
+  ASSERT_EQ(bufs.size(), 0u);
+  os << "12";
+  ASSERT_EQ(bufs.size(), 1u);
+  os << "34";
+  ASSERT_EQ(bufs.size(), 1u);
+  os << "56";
+  ASSERT_EQ(bufs.size(), 2u);
+
+  for (auto& buf : bufs) {
+    buf.Deallocate();
+  }
+}
+
+TEST(RawUvStreamTest, LargeWrite) {
+  SmallVector<uv::Buffer, 4> bufs;
+  raw_uv_ostream os(bufs, 4);
+  os << "123456";
+  ASSERT_EQ(bufs.size(), 2u);
+  ASSERT_EQ(bufs[1].len, 2u);
+  ASSERT_EQ(bufs[1].base[0], '5');
+
+  for (auto& buf : bufs) {
+    buf.Deallocate();
+  }
+}
+
+TEST(RawUvStreamTest, PrevDataWrite) {
+  SmallVector<uv::Buffer, 4> bufs;
+  bufs.emplace_back(uv::Buffer::Allocate(1024));
+  raw_uv_ostream os(bufs, 1024);
+  os << "1234";
+  ASSERT_EQ(bufs.size(), 2u);
+  ASSERT_EQ(bufs[0].len, 1024u);
+  ASSERT_EQ(bufs[1].len, 4u);
+
+  for (auto& buf : bufs) {
+    buf.Deallocate();
+  }
+}
+
+}  // namespace wpi
diff --git a/wpinet/src/test/native/cpp/uv/UvAsyncFunctionTest.cpp b/wpinet/src/test/native/cpp/uv/UvAsyncFunctionTest.cpp
new file mode 100644
index 0000000..19c1faf
--- /dev/null
+++ b/wpinet/src/test/native/cpp/uv/UvAsyncFunctionTest.cpp
@@ -0,0 +1,264 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#include "wpinet/uv/AsyncFunction.h"  // NOLINT(build/include_order)
+
+#include "gtest/gtest.h"  // NOLINT(build/include_order)
+
+#include <thread>
+
+#include "wpinet/uv/Loop.h"
+#include "wpinet/uv/Prepare.h"
+
+namespace wpi::uv {
+
+TEST(UvAsyncFunctionTest, Basic) {
+  int prepare_cb_called = 0;
+  int async_cb_called[2] = {0, 0};
+  int close_cb_called = 0;
+
+  std::thread theThread;
+
+  auto loop = Loop::Create();
+  auto async = AsyncFunction<int(int)>::Create(loop);
+  auto prepare = Prepare::Create(loop);
+
+  loop->error.connect([](Error) { FAIL(); });
+
+  prepare->error.connect([](Error) { FAIL(); });
+  prepare->prepare.connect([&] {
+    if (prepare_cb_called++) {
+      return;
+    }
+    theThread = std::thread([&] {
+      auto call0 = async->Call(0);
+      auto call1 = async->Call(1);
+      ASSERT_EQ(call0.get(), 1);
+      ASSERT_EQ(call1.get(), 2);
+    });
+  });
+  prepare->Start();
+
+  async->error.connect([](Error) { FAIL(); });
+  async->closed.connect([&] { close_cb_called++; });
+  async->wakeup = [&](promise<int> out, int v) {
+    ++async_cb_called[v];
+    if (v == 1) {
+      async->Close();
+      prepare->Close();
+    }
+    out.set_value(v + 1);
+  };
+
+  loop->Run();
+
+  ASSERT_EQ(async_cb_called[0], 1);
+  ASSERT_EQ(async_cb_called[1], 1);
+  ASSERT_EQ(close_cb_called, 1);
+
+  if (theThread.joinable()) {
+    theThread.join();
+  }
+}
+
+TEST(UvAsyncFunctionTest, Ref) {
+  int prepare_cb_called = 0;
+  int val = 0;
+
+  std::thread theThread;
+
+  auto loop = Loop::Create();
+  auto async = AsyncFunction<int(int, int&)>::Create(loop);
+  auto prepare = Prepare::Create(loop);
+
+  prepare->prepare.connect([&] {
+    if (prepare_cb_called++) {
+      return;
+    }
+    theThread = std::thread([&] { ASSERT_EQ(async->Call(1, val).get(), 2); });
+  });
+  prepare->Start();
+
+  async->wakeup = [&](promise<int> out, int v, int& r) {
+    r = v;
+    async->Close();
+    prepare->Close();
+    out.set_value(v + 1);
+  };
+
+  loop->Run();
+
+  ASSERT_EQ(val, 1);
+
+  if (theThread.joinable()) {
+    theThread.join();
+  }
+}
+
+TEST(UvAsyncFunctionTest, Movable) {
+  int prepare_cb_called = 0;
+
+  std::thread theThread;
+
+  auto loop = Loop::Create();
+  auto async =
+      AsyncFunction<std::unique_ptr<int>(std::unique_ptr<int>)>::Create(loop);
+  auto prepare = Prepare::Create(loop);
+
+  prepare->prepare.connect([&] {
+    if (prepare_cb_called++) {
+      return;
+    }
+    theThread = std::thread([&] {
+      auto val = std::make_unique<int>(1);
+      auto val2 = async->Call(std::move(val)).get();
+      ASSERT_NE(val2, nullptr);
+      ASSERT_EQ(*val2, 1);
+    });
+  });
+  prepare->Start();
+
+  async->wakeup = [&](promise<std::unique_ptr<int>> out,
+                      std::unique_ptr<int> v) {
+    async->Close();
+    prepare->Close();
+    out.set_value(std::move(v));
+  };
+
+  loop->Run();
+
+  if (theThread.joinable()) {
+    theThread.join();
+  }
+}
+
+TEST(UvAsyncFunctionTest, CallIgnoreResult) {
+  int prepare_cb_called = 0;
+
+  std::thread theThread;
+
+  auto loop = Loop::Create();
+  auto async =
+      AsyncFunction<std::unique_ptr<int>(std::unique_ptr<int>)>::Create(loop);
+  auto prepare = Prepare::Create(loop);
+
+  prepare->prepare.connect([&] {
+    if (prepare_cb_called++) {
+      return;
+    }
+    theThread = std::thread([&] { async->Call(std::make_unique<int>(1)); });
+  });
+  prepare->Start();
+
+  async->wakeup = [&](promise<std::unique_ptr<int>> out,
+                      std::unique_ptr<int> v) {
+    async->Close();
+    prepare->Close();
+    out.set_value(std::move(v));
+  };
+
+  loop->Run();
+
+  if (theThread.joinable()) {
+    theThread.join();
+  }
+}
+
+TEST(UvAsyncFunctionTest, VoidCall) {
+  int prepare_cb_called = 0;
+
+  std::thread theThread;
+
+  auto loop = Loop::Create();
+  auto async = AsyncFunction<void()>::Create(loop);
+  auto prepare = Prepare::Create(loop);
+
+  prepare->prepare.connect([&] {
+    if (prepare_cb_called++) {
+      return;
+    }
+    theThread = std::thread([&] { async->Call(); });
+  });
+  prepare->Start();
+
+  async->wakeup = [&](promise<void> out) {
+    async->Close();
+    prepare->Close();
+    out.set_value();
+  };
+
+  loop->Run();
+
+  if (theThread.joinable()) {
+    theThread.join();
+  }
+}
+
+TEST(UvAsyncFunctionTest, WaitFor) {
+  int prepare_cb_called = 0;
+
+  std::thread theThread;
+
+  auto loop = Loop::Create();
+  auto async = AsyncFunction<int()>::Create(loop);
+  auto prepare = Prepare::Create(loop);
+
+  prepare->prepare.connect([&] {
+    if (prepare_cb_called++) {
+      return;
+    }
+    theThread = std::thread([&] {
+      ASSERT_FALSE(async->Call().wait_for(std::chrono::milliseconds(10)));
+    });
+  });
+  prepare->Start();
+
+  async->wakeup = [&](promise<int> out) {
+    async->Close();
+    prepare->Close();
+    std::this_thread::sleep_for(std::chrono::milliseconds(100));
+    out.set_value(1);
+  };
+
+  loop->Run();
+
+  if (theThread.joinable()) {
+    theThread.join();
+  }
+}
+
+TEST(UvAsyncFunctionTest, VoidWaitFor) {
+  int prepare_cb_called = 0;
+
+  std::thread theThread;
+
+  auto loop = Loop::Create();
+  auto async = AsyncFunction<void()>::Create(loop);
+  auto prepare = Prepare::Create(loop);
+
+  prepare->prepare.connect([&] {
+    if (prepare_cb_called++) {
+      return;
+    }
+    theThread = std::thread([&] {
+      ASSERT_FALSE(async->Call().wait_for(std::chrono::milliseconds(10)));
+    });
+  });
+  prepare->Start();
+
+  async->wakeup = [&](promise<void> out) {
+    async->Close();
+    prepare->Close();
+    std::this_thread::sleep_for(std::chrono::milliseconds(100));
+    out.set_value();
+  };
+
+  loop->Run();
+
+  if (theThread.joinable()) {
+    theThread.join();
+  }
+}
+
+}  // namespace wpi::uv
diff --git a/wpinet/src/test/native/cpp/uv/UvAsyncTest.cpp b/wpinet/src/test/native/cpp/uv/UvAsyncTest.cpp
new file mode 100644
index 0000000..5dd0f76
--- /dev/null
+++ b/wpinet/src/test/native/cpp/uv/UvAsyncTest.cpp
@@ -0,0 +1,186 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include "wpinet/uv/Async.h"  // NOLINT(build/include_order)
+
+#include "gtest/gtest.h"  // NOLINT(build/include_order)
+
+#include <atomic>
+#include <thread>
+
+#include <wpi/mutex.h>
+
+#include "wpinet/uv/Loop.h"
+#include "wpinet/uv/Prepare.h"
+
+namespace wpi::uv {
+
+TEST(UvAsyncTest, CallbackOnly) {
+  std::atomic_int async_cb_called{0};
+  int prepare_cb_called = 0;
+  int close_cb_called = 0;
+
+  wpi::mutex mutex;
+  mutex.lock();
+
+  std::thread theThread;
+
+  auto loop = Loop::Create();
+  auto async = Async<>::Create(loop);
+  auto prepare = Prepare::Create(loop);
+
+  loop->error.connect([](Error) { FAIL(); });
+
+  prepare->error.connect([](Error) { FAIL(); });
+  prepare->closed.connect([&] { close_cb_called++; });
+  prepare->prepare.connect([&] {
+    if (prepare_cb_called++) {
+      return;
+    }
+    theThread = std::thread([&] {
+      for (;;) {
+        mutex.lock();
+        int n = async_cb_called;
+        mutex.unlock();
+
+        if (n == 3) {
+          break;
+        }
+
+        async->Send();
+
+        std::this_thread::yield();
+      }
+    });
+    mutex.unlock();
+  });
+  prepare->Start();
+
+  async->error.connect([](Error) { FAIL(); });
+  async->closed.connect([&] { close_cb_called++; });
+  async->wakeup.connect([&] {
+    mutex.lock();
+    int n = ++async_cb_called;
+    mutex.unlock();
+
+    if (n == 3) {
+      async->Close();
+      prepare->Close();
+    }
+  });
+
+  loop->Run();
+
+  ASSERT_GT(prepare_cb_called, 0);
+  ASSERT_EQ(async_cb_called, 3);
+  ASSERT_EQ(close_cb_called, 2);
+
+  if (theThread.joinable()) {
+    theThread.join();
+  }
+}
+
+TEST(UvAsyncTest, Data) {
+  int prepare_cb_called = 0;
+  int async_cb_called[2] = {0, 0};
+  int close_cb_called = 0;
+
+  std::thread theThread;
+
+  auto loop = Loop::Create();
+  auto async = Async<int, std::function<void(int)>>::Create(loop);
+  auto prepare = Prepare::Create(loop);
+
+  loop->error.connect([](Error) { FAIL(); });
+
+  prepare->error.connect([](Error) { FAIL(); });
+  prepare->prepare.connect([&] {
+    if (prepare_cb_called++) {
+      return;
+    }
+    theThread = std::thread([&] {
+      async->Send(0, [&](int v) {
+        ASSERT_EQ(v, 0);
+        ++async_cb_called[0];
+      });
+      async->Send(1, [&](int v) {
+        ASSERT_EQ(v, 1);
+        ++async_cb_called[1];
+        async->Close();
+        prepare->Close();
+      });
+    });
+  });
+  prepare->Start();
+
+  async->error.connect([](Error) { FAIL(); });
+  async->closed.connect([&] { close_cb_called++; });
+  async->wakeup.connect([&](int v, std::function<void(int)> f) { f(v); });
+
+  loop->Run();
+
+  ASSERT_EQ(async_cb_called[0], 1);
+  ASSERT_EQ(async_cb_called[1], 1);
+  ASSERT_EQ(close_cb_called, 1);
+
+  if (theThread.joinable()) {
+    theThread.join();
+  }
+}
+
+TEST(UvAsyncTest, DataRef) {
+  int prepare_cb_called = 0;
+  int val = 0;
+
+  std::thread theThread;
+
+  auto loop = Loop::Create();
+  auto async = Async<int, int&>::Create(loop);
+  auto prepare = Prepare::Create(loop);
+
+  prepare->prepare.connect([&] {
+    if (prepare_cb_called++) {
+      return;
+    }
+    theThread = std::thread([&] { async->Send(1, val); });
+  });
+  prepare->Start();
+
+  async->wakeup.connect([&](int v, int& r) {
+    r = v;
+    async->Close();
+    prepare->Close();
+  });
+
+  loop->Run();
+
+  ASSERT_EQ(val, 1);
+
+  if (theThread.joinable()) {
+    theThread.join();
+  }
+}
+
+}  // namespace wpi::uv
diff --git a/wpinet/src/test/native/cpp/uv/UvBufferTest.cpp b/wpinet/src/test/native/cpp/uv/UvBufferTest.cpp
new file mode 100644
index 0000000..da6a63c
--- /dev/null
+++ b/wpinet/src/test/native/cpp/uv/UvBufferTest.cpp
@@ -0,0 +1,48 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#include "wpinet/uv/Buffer.h"  // NOLINT(build/include_order)
+
+#include "gtest/gtest.h"  // NOLINT(build/include_order)
+
+namespace wpi::uv {
+
+TEST(UvSimpleBufferPoolTest, ConstructDefault) {
+  SimpleBufferPool<> pool;
+  auto buf1 = pool.Allocate();
+  ASSERT_EQ(buf1.len, 4096u);  // NOLINT
+  pool.Release({&buf1, 1});
+}
+
+TEST(UvSimpleBufferPoolTest, ConstructSize) {
+  SimpleBufferPool<4> pool{8192};
+  auto buf1 = pool.Allocate();
+  ASSERT_EQ(buf1.len, 8192u);  // NOLINT
+  pool.Release({&buf1, 1});
+}
+
+TEST(UvSimpleBufferPoolTest, ReleaseReuse) {
+  SimpleBufferPool<4> pool;
+  auto buf1 = pool.Allocate();
+  auto buf1copy = buf1;
+  auto origSize = buf1.len;
+  buf1.len = 8;
+  pool.Release({&buf1, 1});
+  ASSERT_EQ(buf1.base, nullptr);
+  auto buf2 = pool.Allocate();
+  ASSERT_EQ(buf1copy.base, buf2.base);
+  ASSERT_EQ(buf2.len, origSize);
+  pool.Release({&buf2, 1});
+}
+
+TEST(UvSimpleBufferPoolTest, ClearRemaining) {
+  SimpleBufferPool<4> pool;
+  auto buf1 = pool.Allocate();
+  pool.Release({&buf1, 1});
+  ASSERT_EQ(pool.Remaining(), 1u);
+  pool.Clear();
+  ASSERT_EQ(pool.Remaining(), 0u);
+}
+
+}  // namespace wpi::uv
diff --git a/wpinet/src/test/native/cpp/uv/UvGetAddrInfoTest.cpp b/wpinet/src/test/native/cpp/uv/UvGetAddrInfoTest.cpp
new file mode 100644
index 0000000..5ad33b2
--- /dev/null
+++ b/wpinet/src/test/native/cpp/uv/UvGetAddrInfoTest.cpp
@@ -0,0 +1,109 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include "wpinet/uv/GetAddrInfo.h"  // NOLINT(build/include_order)
+
+#include "gtest/gtest.h"  // NOLINT(build/include_order)
+
+#include "wpinet/uv/Loop.h"
+
+#define CONCURRENT_COUNT 10
+
+namespace wpi::uv {
+
+TEST(UvGetAddrInfoTest, BothNull) {
+  int fail_cb_called = 0;
+
+  auto loop = Loop::Create();
+  loop->error.connect([&](Error err) {
+    ASSERT_EQ(err.code(), UV_EINVAL);
+    fail_cb_called++;
+  });
+
+  GetAddrInfo(
+      loop, [](const addrinfo&) { FAIL(); }, "");
+  loop->Run();
+  ASSERT_EQ(fail_cb_called, 1);
+}
+
+TEST(UvGetAddrInfoTest, FailedLookup) {
+  int fail_cb_called = 0;
+
+  auto loop = Loop::Create();
+  loop->error.connect([&](Error err) {
+    ASSERT_EQ(fail_cb_called, 0);
+    ASSERT_LT(err.code(), 0);
+    fail_cb_called++;
+  });
+
+  // Use a FQDN by ending in a period
+  GetAddrInfo(
+      loop, [](const addrinfo&) { FAIL(); }, "xyzzy.xyzzy.xyzzy.");
+  loop->Run();
+  ASSERT_EQ(fail_cb_called, 1);
+}
+
+TEST(UvGetAddrInfoTest, Basic) {
+  int getaddrinfo_cbs = 0;
+
+  auto loop = Loop::Create();
+  loop->error.connect([](Error) { FAIL(); });
+
+  GetAddrInfo(
+      loop, [&](const addrinfo&) { getaddrinfo_cbs++; }, "localhost");
+
+  loop->Run();
+
+  ASSERT_EQ(getaddrinfo_cbs, 1);
+}
+
+#ifndef _WIN32
+TEST(UvGetAddrInfoTest, Concurrent) {
+  int getaddrinfo_cbs = 0;
+  int callback_counts[CONCURRENT_COUNT];
+
+  auto loop = Loop::Create();
+  loop->error.connect([](Error) { FAIL(); });
+
+  for (int i = 0; i < CONCURRENT_COUNT; i++) {
+    callback_counts[i] = 0;
+    GetAddrInfo(
+        loop,
+        [i, &callback_counts, &getaddrinfo_cbs](const addrinfo&) {
+          callback_counts[i]++;
+          getaddrinfo_cbs++;
+        },
+        "localhost");
+  }
+
+  loop->Run();
+
+  for (int i = 0; i < CONCURRENT_COUNT; i++) {
+    ASSERT_EQ(callback_counts[i], 1);
+  }
+}
+#endif
+
+}  // namespace wpi::uv
diff --git a/wpinet/src/test/native/cpp/uv/UvGetNameInfoTest.cpp b/wpinet/src/test/native/cpp/uv/UvGetNameInfoTest.cpp
new file mode 100644
index 0000000..707f037
--- /dev/null
+++ b/wpinet/src/test/native/cpp/uv/UvGetNameInfoTest.cpp
@@ -0,0 +1,74 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include "wpinet/uv/GetNameInfo.h"  // NOLINT(build/include_order)
+
+#include "gtest/gtest.h"  // NOLINT(build/include_order)
+
+#include "wpinet/uv/Loop.h"
+
+namespace wpi::uv {
+
+TEST(UvGetNameInfoTest, BasicIp4) {
+  int getnameinfo_cbs = 0;
+
+  auto loop = Loop::Create();
+  loop->error.connect([](Error) { FAIL(); });
+
+  GetNameInfo4(
+      loop,
+      [&](const char* hostname, const char* service) {
+        ASSERT_NE(hostname, nullptr);
+        ASSERT_NE(service, nullptr);
+        getnameinfo_cbs++;
+      },
+      "127.0.0.1", 80);
+
+  loop->Run();
+
+  ASSERT_EQ(getnameinfo_cbs, 1);
+}
+
+TEST(UvGetNameInfoTest, BasicIp6) {
+  int getnameinfo_cbs = 0;
+
+  auto loop = Loop::Create();
+  loop->error.connect([](Error) { FAIL(); });
+
+  GetNameInfo6(
+      loop,
+      [&](const char* hostname, const char* service) {
+        ASSERT_NE(hostname, nullptr);
+        ASSERT_NE(service, nullptr);
+        getnameinfo_cbs++;
+      },
+      "::1", 80);
+
+  loop->Run();
+
+  ASSERT_EQ(getnameinfo_cbs, 1);
+}
+
+}  // namespace wpi::uv
diff --git a/wpinet/src/test/native/cpp/uv/UvLoopWalkTest.cpp b/wpinet/src/test/native/cpp/uv/UvLoopWalkTest.cpp
new file mode 100644
index 0000000..eee0f99
--- /dev/null
+++ b/wpinet/src/test/native/cpp/uv/UvLoopWalkTest.cpp
@@ -0,0 +1,69 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include "wpinet/uv/Loop.h"  // NOLINT(build/include_order)
+
+#include "gtest/gtest.h"  // NOLINT(build/include_order)
+
+#include "wpinet/uv/Timer.h"
+
+namespace wpi::uv {
+
+TEST(UvLoopTest, Walk) {
+  int seen_timer_handle = 0;
+
+  auto loop = Loop::Create();
+  auto timer = Timer::Create(loop);
+
+  loop->error.connect([](Error) { FAIL(); });
+
+  timer->error.connect([](Error) { FAIL(); });
+
+  timer->timeout.connect([&, theTimer = timer.get()] {
+    theTimer->GetLoopRef().Walk([&](Handle& it) {
+      if (&it == timer.get()) {
+        seen_timer_handle++;
+      }
+    });
+    theTimer->Close();
+  });
+  timer->Start(Timer::Time{1});
+
+  // Start event loop, expect to see the timer handle
+  ASSERT_EQ(seen_timer_handle, 0);
+  loop->Run();
+  ASSERT_EQ(seen_timer_handle, 1);
+
+  // Loop is finished, should not see our timer handle
+  seen_timer_handle = 0;
+  loop->Walk([&](Handle& it) {
+    if (&it == timer.get()) {
+      seen_timer_handle++;
+    }
+  });
+  ASSERT_EQ(seen_timer_handle, 0);
+}
+
+}  // namespace wpi::uv
diff --git a/wpinet/src/test/native/cpp/uv/UvTimerTest.cpp b/wpinet/src/test/native/cpp/uv/UvTimerTest.cpp
new file mode 100644
index 0000000..7377ab1
--- /dev/null
+++ b/wpinet/src/test/native/cpp/uv/UvTimerTest.cpp
@@ -0,0 +1,69 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#include "wpinet/uv/Timer.h"  // NOLINT(build/include_order)
+
+#include "gtest/gtest.h"
+
+namespace wpi::uv {
+
+TEST(UvTimerTest, StartAndStop) {
+  auto loop = Loop::Create();
+  auto handleNoRepeat = Timer::Create(loop);
+  auto handleRepeat = Timer::Create(loop);
+
+  bool checkTimerNoRepeatEvent = false;
+  bool checkTimerRepeatEvent = false;
+
+  handleNoRepeat->error.connect([](Error) { FAIL(); });
+  handleRepeat->error.connect([](Error) { FAIL(); });
+
+  handleNoRepeat->timeout.connect(
+      [&checkTimerNoRepeatEvent, handle = handleNoRepeat.get()] {
+        ASSERT_FALSE(checkTimerNoRepeatEvent);
+        checkTimerNoRepeatEvent = true;
+        handle->Stop();
+        handle->Close();
+        ASSERT_TRUE(handle->IsClosing());
+      });
+
+  handleRepeat->timeout.connect(
+      [&checkTimerRepeatEvent, handle = handleRepeat.get()] {
+        if (checkTimerRepeatEvent) {
+          handle->Stop();
+          handle->Close();
+          ASSERT_TRUE(handle->IsClosing());
+        } else {
+          checkTimerRepeatEvent = true;
+          ASSERT_FALSE(handle->IsClosing());
+        }
+      });
+
+  handleNoRepeat->Start(Timer::Time{0}, Timer::Time{0});
+  handleRepeat->Start(Timer::Time{0}, Timer::Time{1});
+
+  ASSERT_TRUE(handleNoRepeat->IsActive());
+  ASSERT_FALSE(handleNoRepeat->IsClosing());
+
+  ASSERT_TRUE(handleRepeat->IsActive());
+  ASSERT_FALSE(handleRepeat->IsClosing());
+
+  loop->Run();
+
+  ASSERT_TRUE(checkTimerNoRepeatEvent);
+  ASSERT_TRUE(checkTimerRepeatEvent);
+}
+
+TEST(UvTimerTest, Repeat) {
+  auto loop = Loop::Create();
+  auto handle = Timer::Create(loop);
+
+  handle->SetRepeat(Timer::Time{42});
+  ASSERT_EQ(handle->GetRepeat(), Timer::Time{42});
+  handle->Close();
+
+  loop->Run();  // forces close callback to run
+}
+
+}  // namespace wpi::uv