diff --git a/Jamfile b/Jamfile new file mode 100644 index 00000000..bdb98b28 --- /dev/null +++ b/Jamfile @@ -0,0 +1,32 @@ +project libsecondlife + : requirements include + : default-build debug + ; + +lib openssl : : ssl ; +lib libcrypto : : crypto ; +lib libsocket : : socket ; +lib libnsl : : nsl ; +lib boostthread : : boost_thread ; + +if $(UNIX) +{ + switch $(JAMUNAME) + { + case SunOS* : + { + SOCKET_LIBS = libsocket libnsl ; + } + } +} + +lib secondlife + : [ glob src/*.cpp ] + openssl + libcrypto + boostthread + $(SOCKET_LIBS) + : multi + ; + +build-project test_app ; diff --git a/bin/comm.dat b/bin/comm.dat new file mode 100644 index 00000000..66a406ed Binary files /dev/null and b/bin/comm.dat differ diff --git a/bin/keywords.txt b/bin/keywords.txt new file mode 100644 index 00000000..f0bd6ef2 --- /dev/null +++ b/bin/keywords.txt @@ -0,0 +1,1480 @@ +X +Y +Z +VotedForCandidate +AddFlags +Everyone +ReservedNewbie +MapData +AddItem +MeanCollision +RezScript +AvatarSitResponse +InventoryAssetResponse +KillObject +ProposalID +SerialNum +Duration +ScriptQuestion +AddCircuitCode +UseCircuitCode +ViewerCircuitCode +ScriptAnswerYes +PartnerID +DirLandQuery +TeleportStart +EmpoweredBlock +LogMessages +DropGroupIM +AboutText +VisualParam +ID +UUIDNameRequest +UUIDGroupNameRequest +MoneyTransactionsRequest +GroupAccountTransactionsRequest +MapNameRequest +MailTaskSimRequest +UpdateSimulator +BillableFactor +ObjectBonusFactor +EnableSimulator +DisableSimulator +ConfirmEnableSimulator +LayerType +ParcelOverlay +AdjustBalance +GroupOwned +IP +ChatFromViewer +FirstLogin +GroupTitle +MapLayerReply +CompoundMsgID +CameraConstraint +DownloadTotals +ChatMessage +ErrorValue +GenCounter +FrozenData +URLBlock +ChildAgentDying +To +ParcelDirFeeCurrent +ObjectDuplicate +InventoryData +ReplyData +ResetList +SimulatorPauseState +MediaID +RedirectGridX +RedirectGridY +TransferID +TexturesChanged +UserLookAt +TestBlock1 +SensedData +UpdateBlock +EmpoweredID +ClassifiedGodDelete +LocationPos +ObjectGrabUpdate +TaxDate +StartDateTime +ObjectUpdateCached +Packets +FailureType +UpdateGroupInfo +InventoryFile +ObjectPermissions +RevokePermissions +UpdateFlags +RezSelected +AutoPilot +UpdateMuteListEntry +RemoveMuteListEntry +SimStatusInDatabase +SetSimPresenceInDatabase +CameraProperty +GroupRecallBallot +BrushSize +SimulatorSetMap +RegionPresenceRequestByRegionID +TransferEnergy +ParcelObjectOwnersReply +GroupMembersReply +GroupOfficersAndMembersReply +RequestRegionInfo +AABBMax +RequestPayPrice +SimulatorPresentAtLocation +AgentRequestSit +AABBMin +ClassifiedFlags +ControlFlags +SpaceLocationTeleportRequest +LeaderBoardRequest +ScriptTeleportRequest +DateUTC +RequestResult +ReputationAgentAssign +CanAcceptAgents +ObjectSaleInfo +KillChildAgents +Balance +DerezContainer +ObjectData +CameraAtAxis +InfoBlock +OwnershipCost +AvatarNotesUpdate +PID +TimeString +DirPopularReply +TerrainHeightRange00 +SimData +TerrainHeightRange01 +TerrainHeightRange10 +TerrainHeightRange11 +UpdateInventoryItem +MoveInventoryItem +CopyInventoryItem +RemoveInventoryItem +PathTwistBegin +CRC +AttachmentPoint +TelehubBlock +FOVBlock +StartLocationData +PositionData +TimeSinceLast +MapImage +Objects +URL +CreationDate +JointPivot +RateeID +FPS +HasTelehub +PathEnd +ScriptDataReply +MapBlockReply +PropertiesData +ViewerEffect +FreezeUser +ObjectGrab +ToAgentID +ProxyBlock +SimulatorMapUpdate +TransferPacket +ObjectName +OriginalName +CompletePingCheck +OnlineStatus +TrackOnlineStatus +IgnoreOnlineStatus +ObjectDrop +UseBigPackets +ParcelAccessListReply +RpcChannelReply +RegionPresenceResponse +AgentPresenceResponse +CharterMember +EdgeData +NameData +SimName +UserReport +DownloadPriority +ToAgentId +Mag +DirPopularQuery +ParcelPropertiesRequestByID +ObjectLink +RpcScriptReplyInbound +BoardData +RezData +RemoveInventoryObjects +Officer +GroupProposalBallot +RPCServerIP +Far +GodSessionID +ViewerDigest +FLAboutText +RegionHandshakeReply +GroupActiveProposalItemReply +MapItemReply +Seconds +UpdateUserInfo +AggregatePermTexturesOwner +Set +Key +NewName +AgentID +OnlineStatusRequest +DataAgentAccessRequest +AvatarNotesRequest +EventNotificationRemoveRequest +Arc +NewFolderID +RegionX +RegionY +UUIDSoundTriggerFar +RequestData +Msg +Top +MiscStats +Pos +ImageID +DataPacket +ObjectDehinge +You +ScriptControlChange +SetCPURatio +NameValueData +AtomicPassObject +ViewerFrozenMessage +HealthMessage +LogTextMessage +TimeDilation +Contribution +SetGroupContribution +Offline +SecPerDay +Members +FailedResends +CameraCenter +CameraLeftAxis +ExBlock +ScriptBlock +Channel +NetTest +DiscardLevel +LayerID +RatorID +GrabOffset +SimPort +PricePerMeter +RegionFlags +VoteResult +ParcelDirFeeEstimate +ModifyBlock +InventoryBlock +ReplyBlock +RequireMask +ValidUntil +VelocityInterpolateOn +ClassifiedDelete +FLImageID +AllowPublish +SitName +OfficerTitle +RegionsVisited +RecallID +DirClassifiedReply +AvatarClassifiedReply +ReputationIndividualReply +MediaURL +CompleteAgentMovement +SpaceIP +ClassifiedID +LocalID +RemoveItem +LogFailedMoneyTransaction +ViewerStartAuction +StartAuction +NameValueName +AngVelX +DuplicateFlags +AngVelY +AngVelZ +TextColor +SlaveID +Charter +TargetBlock +AlertData +MaxObjects +CheckParcelAuctions +ParcelAuctions +NameValuePair +RemoveNameValuePair +GetNameValuePair +BulkUpdateInventory +UpdateTaskInventory +RemoveTaskInventory +MoveTaskInventory +RequestTaskInventory +ReplyTaskInventory +DeclineInventory +AggregatePermInventory +SimulatorInfo +MoneyTransactionsReply +GroupAccountTransactionsReply +MultiTaskSimReply +WearableData +StatisticsData +AccessOK +Enabled +Savings +SimulatorLoad +InternalRegionIP +ExternalRegionIP +CreateGroupRequest +JoinGroupRequest +LeaveGroupRequest +InviteGroupRequest +LiveHelpGroupRequest +ServerVersion +PriceParcelClaimFactor +BillableArea +ObjectID +ObjectFlagUpdate +ActiveOnly +RequestInventoryAsset +RedoLand +TravelAccess +ChangedGrid +Details +SaleType +ObjectExportReply +EconomyData +HeadRotation +DeleteOnCompletion +PublicPort +CurrentTaxes +DirClassifiedQuery +RequestParcelTransfer +ObjectCapacity +RequestID +GranterName +RequestXfer +ObjectTaxCurrent +LightTaxCurrent +LandTaxCurrent +GroupTaxCurrent +FetchInventoryDescendents +InventoryDescendents +Descendents +PurgeInventoryDescendents +ShowDir +Timestamp +GlobalPos +LimitedToEstate +GrabOffsetInitial +IsTrial +FinalizeLogout +ObjectDuplicateOnRay +GroupMembershipCount +MethodData +ActivateGestures +DeactivateGestures +ProposalData +PosGlobal +SearchID +RezMultipleAttachmentsFromInv +SearchName +VersionString +CreateGroupReply +ActualArea +RevokedID +Message +ClickAction +AssetUploadComplete +EstimatedTaxes +RequestType +UUID +BaseMask +NetBlock +GlobalX +GlobalY +CopyRotates +KickUserAck +TopPick +SessionID +GlobalZ +CallVote +DeclineFriendship +FormFriendship +TerminateFriendship +TaskData +SourceFilename +ProfileBegin +MoneyDetailsRequest +Request +GroupAccountDetailsRequest +GroupActiveProposalsRequest +VoteQuorum +StringValue +ClosestSimulator +Version +OtherCount +ChatData +GhostData +IsGroupOwned +EnergyEfficiency +MaxPlace +PickInfoUpdate +PickDelete +ScriptReset +Requester +RevokerID +ElectionID +ForSale +NearestLandingRegionReply +RecordAgentPresence +EraseAgentPresence +ParcelID +Godlike +TotalDebits +Direction +Appearance +HealthData +LeftAxis +PositionBlock +LocationBlock +ObjectImage +TerrainStartHeight00 +TerrainStartHeight01 +TerrainStartHeight10 +ObjectHinge +TerrainStartHeight11 +MetersPerGrid +WaterHeight +FetchInventoryReply +MoneySummaryReply +GroupAccountSummaryReply +AttachedSound +GodKickUser +PickName +SkillFlags +ParcelGodReserveForNewbie +SubType +ObjectCount +RegionPresenceRequestByHandle +RezSingleAttachmentFromInv +ChildAgentUpdate +XPos +YPos +ZPos +ToID +ViewerPort +IsOwnerGroup +AgentHeightWidth +VerticalAngle +WearableType +AggregatePermNextOwner +ShowInList +PositionSuggestion +UpdateParcel +ClearAgentSessions +SetAlwaysRun +NVPair +ObjectSpinStart +UseEstateSun +LogoutBlock +OwnerObjects +OtherObjects +RelayLogControl +RegionID +Creator +ViewerRegion +ProposalText +DirEventsReply +EventInfoReply +GroupElectionInfoReply +UserInfoReply +PathRadiusOffset +SessionInfo +TextureData +ChatPass +TargetID +DefaultPayPrice +UserLocation +RegionIP +LandmarkID +InitiateDownload +Name +OtherCleanTime +TeleportPriceExponent +Gain +VelX +PacketAck +PathSkew +Negative +VelY +SimulatorShutdownRequest +NearestLandingRegionRequest +VelZ +OtherID +MapLayerRequest +PatchVersion +ObjectScale +TargetIP +Redo +MoneyBalance +TrackAgent +MaxX +Data +MaxY +TextureAnim +ReturnIDs +Date +GestureUpdate +AgentWearablesUpdate +AgentDataUpdate +Hash +Left +Mask +ForceMouselook +RequestLocationGetAccess +Success +ObjectGroup +SunHour +MinX +ScriptSensorReply +MinY +Command +Desc +AttachmentNeedsSave +HistoryItemData +AgentCachedTexture +East +Subject +GodExpungeUser +QueryReplies +ObjectCategory +Time +ParentID +Ping +Perp +Code +InvType +AgentFOV +BulkMoneyTransfer +Audible +AuctionData +IDBlock +ReputationData +West +ElectionData +Undo +Info +CheckCircuitsTime +Area +Behavior +SimCrashed +Text +AgentToNewRegion +PriceGroupCreate +ObjectShape +PosX +PosY +MuteCRC +PosZ +Size +FromAddress +Body +FileData +List +KickUser +RunTime +RpcScriptRequestInboundForward +More +Majority +SenderID +MetersTraveled +Stat +FromAgentID +Item +SoundID +User +RemoteInfos +Vote +Prey +UsecSinceStart +RayStart +ParcelData +CameraUpAxis +ScriptDialog +MasterParcelData +Invalid +MinPlace +ProfileCurve +ParcelAccessListUpdate +MuteListUpdate +SendPacket +SendXferPacket +LastName +From +Port +MemberTitle +LogParcelChanges +DeRezObject +IsTemporary +IsComplete +InsigniaID +CheckFlags +TransferPriority +EventID +FromAgentId +Type +ReportData +LeaderBoardData +RequestBlock +GrantData +DetachAttachmentIntoInv +GodLevel +StartGroupIM +PayPriceReply +QueryID +CameraEyeOffset +AgentPosition +GrabPosition +GrantModification +RevokeModification +OnlineNotification +OfflineNotification +SendPostcard +RequestFlags +MoneyHistoryRequest +MoneySummaryRequest +GroupMoneyHistoryRequest +GroupAccountSummaryRequest +ParamValue +GroupVoteHistoryRequest +Checksum +MaxAgents +NumAttachments +CreateNewOutfitAttachments +RegionHandle +TeleportProgress +AgentQuitCopy +LocationValid +ToViewer +ParcelName +InviteOfficers +PriceObjectRent +ConnectAgentToUserserver +OfferCallingCard +AgentAccess +AcceptCallingCard +DeclineCallingCard +DataHomeLocationReply +EventLocationReply +UserLoginLocationReply +UserSimLocationReply +SpaceLoginLocationReply +TerseDateID +ObjectOwner +AssetID +AlertMessage +EstateOwnerMessage +ParcelMediaCommandMessage +Auction +Category +FilePath +ItemFlags +Invoice +IntervalDays +PathScaleX +FromTaskID +TimeInfo +PathScaleY +PublicCount +ParcelJoin +SimulatorBlock +UserBlock +GroupID +AgentVel +RequestImage +NetStats +AgentPos +AgentSit +Material +ObjectDeGrab +VelocityInterpolateOff +AuthorizedBuyerID +RemoveMemberFromGroup +GroupIM +AvatarPropertiesReply +GroupPropertiesReply +GroupProfileReply +Participants +SimOwner +SalePrice +Animation +CurrentDividend +OwnerID +NearestLandingRegionUpdate +PassToAgent +PreyAgent +SimStats +Options +LogoutReply +ObjectLocalID +Dropped +Destination +MasterID +TransferData +WantToMask +AvatarData +ParcelSelectObjects +TopObjects +LogLogin +CreatorID +Summary +BuyObjectInventory +FetchInventory +InventoryID +PacketNumber +SetFollowCamProperties +ClearFollowCamProperties +SimulatorThrottleSettings +SequenceID +DataServerLogout +NameValue +PathShearX +PathShearY +ElectionType +Velocity +SecPerYear +FirstName +AttachedSoundGainChange +LocationID +Running +ObjectImportReply +AgentThrottle +NeighborList +PathTaperX +PathTaperY +GranterBlock +UseCachedMuteList +FailStats +StartGroupRecall +Tempfile +FounderName +BuyerID +DirPeopleReply +TransferInfo +AvatarPickerRequestBackend +AvatarPropertiesRequestBackend +UpdateData +ReporterID +GranterID +ButtonLabel +WantToText +ReportType +DataBlock +SimulatorReady +AnimationSourceList +RefreshViewer +SubscribeLoad +UnsubscribeLoad +Packet +UndoLand +SimAccess +MembershipFee +CreateInventoryFolder +UpdateInventoryFolder +MoveInventoryFolder +RemoveInventoryFolder +MoneyData +ObjectDeselect +NewAssetID +ObjectAdd +RayEndIsIntersection +CompleteAuction +CircuitCode +AgentMovementComplete +ViewerIP +Header +GestureFlags +XferID +StatValue +PickID +TaskID +GridsPerEdge +RayEnd +Throttles +UpAxis +AgentTextures +Radius +OffCircuit +Access +SquareMetersCredit +Filename +SecuredTemplateChecksumRequest +TemplateChecksumRequest +AgentPresenceRequest +ClassifiedInfoRequest +ParcelInfoRequest +TeleportLandmarkRequest +CommandID +MovedIntoSimulator +ChatFromSimulator +EventInfoRequest +PickInfoRequest +DirPeopleQuery +MoneyBalanceRequest +GroupElectionInfoRequest +GroupMembersRequest +TextureID +GroupOfficersAndMembersRequest +OldFolderID +UserInfoRequest +Handle +StartParcelRenameAck +StateLoad +ButtonIndex +CurrentElectionID +GetScriptRunning +SetScriptRunning +Health +FileID +CircuitInfo +ObjectBuy +ProfileEnd +Effect +TestMessage +ScriptMailRegistration +AgentSetAppearance +AvatarAppearance +RegionData +RequestingRegionData +LandingRegionData +SitTransform +TerrainBase0 +SkillsMask +AtAxis +TerrainBase1 +Reason +TerrainBase2 +TerrainBase3 +Params +PingID +Height +Region +MoneyHistoryReply +GroupMoneyHistoryReply +TelehubInfo +StateSave +AgentAnimation +AvatarAnimation +LogDwellTime +ParcelGodMarkAsContent +UsePhysics +JointType +TaxEstimate +ObjectTaxEstimate +LightTaxEstimate +TeleportLandingStatusChanged +LandTaxEstimate +GroupTaxEstimate +Buttons +Sender +Dialog +DestID +PricePublicObjectDelete +ObjectDelete +Delete +EventGodDelete +LastTaxDate +MapImageID +EndDateTime +TerrainDetail0 +TerrainDetail1 +TerrainDetail2 +TerrainDetail3 +Offset +ObjectDelink +TargetObject +IsEstateManager +CancelAuction +ObjectDetach +Compressed +PathBegin +BypassRaycast +WinnerID +ChannelType +NumberNonExemptMembers +NonExemptMembers +Agents +SimulatorStart +Enable +RevokedBlock +MemberData +ImageNotInDatabase +StartDate +AnimID +Serial +GroupElectionBallot +ControlPort +ModifyLand +Digest +Victim +Script +TemplateChecksumReply +PickInfoReply +MoneyBalanceReply +RoutedMoneyBalanceReply +RegionInfo +Sequence +GodUpdateRegionInfo +LocalX +LocalY +StartAnim +Location +Action +SearchDir +Active +TransferRequest +ScriptSensorRequest +MoneyTransferRequest +EjectGroupMemberRequest +MapPopularRequest +SkillsText +Resent +Center +SharedData +PSBlock +UUIDNameBlock +Viewer +Method +TouchName +CandidateID +GodlikeMessage +SystemMessage +BodyRotation +StartGroupElection +SearchRegions +Ignore +ConversationData +AnimationData +StatID +ItemID +AvatarStatisticsReply +ScriptDialogReply +RegionIDAndHandleReply +CameraAtOffset +VoteID +ParcelGodForceOwner +InviteData +CandidateData +PCode +SearchPos +PreyID +TerrainLowerLimit +EventFlags +TallyVotes +GroupInfoUpdated +Result +LookAt +PayButton +SelfCount +PacketCount +ParcelBuyPass +SimHandle +OldItemID +RegionPort +PriceEnergyUnit +Bitmap +TrackAgentSession +CacheMissType +VFileID +Response +GroupInsigniaID +FromID +Online +KickFlags +SysCPU +EMail +InviteMembers +IncludeMembers +AggregatePermTextures +ChatChannel +ReturnID +ObjectAttach +TargetPort +ObjectSpinStop +FullID +ActivateGroup +SysGPU +StartLure +SysRAM +ObjectPosition +SitPosition +StartTime +BornOn +CameraCollidePlane +EconomyDataRequest +TeleportLureRequest +FolderID +RegionHandleRequest +GestureRequest +ScriptDataRequest +AgentWearablesRequest +MapBlockRequest +LureID +CopyCenters +RegisterNewAgent +ParamList +InventorySerial +EdgeDataPacket +AvatarPickerReply +ParcelDwellReply +IsForSale +MuteID +MeanCollisionAlert +CanAcceptTasks +ItemData +AnimationList +PassObject +Reputation +IntValue +TargetType +Amount +UpdateAttachment +RemoveAttachment +HeightWidthBlock +RequestObjectPropertiesFamily +ObjectPropertiesFamily +UserData +SessionBlock +IsReadable +ReputationMax +PathCurve +ReputationMin +Status +AlreadyVoted +ElectionInitiator +PlacesReply +DirPlacesReply +ParcelBuy +DirFindQueryBackend +DirPlacesQueryBackend +DirPeopleQueryBackend +DirGroupsQueryBackend +DirClassifiedQueryBackend +DirPicksQueryBackend +DirLandQueryBackend +DirPopularQueryBackend +SnapshotID +Aspect +LogoutDemand +HistoryData +VoteData +EstimatedDividend +VoteCast +EveryoneMask +SetSunPhase +ObjectSpinUpdate +MaturePublish +UseExistingAsset +TeleportCancel +UnixTime +QueryFlags +LastExecFroze +AlwaysRun +Bottom +ButtonData +SoundData +ViewerStats +RegionHandshake +Description +ObjectDescription +UUIDNameReply +UUIDGroupNameReply +SaveAssetIntoInventory +UserInfo +AnimSequenceID +NVPairs +ParcelAccessListRequest +UserListRequest +MuteListRequest +StartPeriod +RpcChannelRequest +PlacesQuery +DirPlacesQuery +Distance +SortOrder +Hunter +SunAngVelocity +InventoryUpdate +ImagePacket +BinaryBucket +StartGroupProposal +PhysicsTime +EnergyLevel +PriceForListing +Scale +ParentEstateID +Extra2 +Throttle +SimIP +GodID +TeleportMinPrice +VoteItem +ObjectRotation +SitRotation +SnapSelection +TerrainRaiseLimit +SourceName +TerrainRaiseLimit +Quorum +TokenBlock +AgentBlock +CommandBlock +PricePublicObjectDecay +SpawnPointPos +AttachedSoundCutoffRadius +VolumeDetail +TasksPaused +Range +FromAgentName +AddModifyAbility +RemoveModifyAbility +PublicIP +TeleportFailed +OnlineStatusReply +DataAgentAccessReply +RequestLocationGetAccessReply +RequestAvatarInfo +PreloadSound +ScreenshotID +OldestUnacked +SimulatorIP +ObjectImport +MoneyMax +Value +JointAxisOrAnchor +Test0 +MoneyMin +Test1 +Test2 +SunPhase +Place +Phase +ParcelDivide +PriceObjectClaim +VoteTime +Field +Ratio +JoinGroupReply +LiveHelpGroupReply +Agent +Score +ExpungeData +Image +ObjectClickAction +Delta +InitiateUpload +Parameter +Flags +Plane +Width +VoteText +Right +DirFindQuery +Textures +EventData +Final +TelehubPos +ReportAutosaveCrash +Reset +CreateTrustedCircuit +DenyTrustedCircuit +Codec +Level +Modal +ChildAgentUnknown +LandingType +ScriptRunningReply +MoneyDetailsReply +Reply +TelehubRot +RequestFriendship +AcceptFriendship +GroupAccountDetailsReply +DwellInfo +AgentResume +ItemType +MailFilter +Disconnect +SimPosition +Index +BaseFilename +SimFilename +LastOwnerID +EmailMessageRequest +MapItemRequest +AgentCount +InitializeLure +HelloBlock +FuseBlock +MessageBlock +ClassifiedInfoUpdate +RegionPos +ParcelMediaUpdate +GridX +GridY +AuctionID +VoteType +CategoryID +Token +AggregatePerms +StartParcelRemoveAck +ObjectSelect +ForceObjectSelect +Price +SunDirection +ChangeInventoryItemFlags +Force +ParcelObjectBonus +TransactionBlock +PowersMask +Stamp +TotalCredits +State +TextureIndex +SimPaused +InviteeID +ParcelReclaim +Money +PathTwist +AuthBuyerID +Color +SourceType +World +QueryData +QueryPrice +Users +SysOS +Notes +AvatarID +FounderID +StipendEstimate +LocationLookAt +Sound +Cover +TextureEntry +SquareMetersCommitted +ChannelID +Dwell +North +AgentUpdate +PickGodDelete +UpdateInventoryItemAsset +HostName +PriceParcelClaim +ParcelClaim +ProfileHollow +Count +South +Entry +ObjectUpdateCompressed +Group +AgentPause +InternalScriptMail +FindAgent +AgentData +FolderData +AssetBlock +CloseCircuit +LogControl +TeleportFinish +PathRevolutions +ClassifiedInfoReply +ParcelInfoReply +AutosaveData +SetStartLocation +PassHours +AttachmentPt +ParcelFlags +NumVotes +AvatarPickerRequest +TeleportLocationRequest +DataHomeLocationRequest +EventNotificationAddRequest +ParcelDwellRequest +ViewerLoginLocationRequest +ViewerSimLocationRequest +EventLocationRequest +EndPeriod +SetStartLocationRequest +UserLoginLocationRequest +QueryStart +AvatarTextureUpdate +RequestGrantedProxies +GrantedProxies +RPCServerPort +Bytes +Extra +ForceScriptControlRelease +ParcelRelease +VFileType +ImageData +SpaceServerSimulatorTimeMessage +SimulatorViewerTimeMessage +Rotation +Selection +TransactionData +OperationData +AgentName +ParcelDeedToGroup +DirPicksReply +AvatarPicksReply +AgentInfo +MoneyTransferBackend +NextOwnerMask +MuteData +PassPrice +SourceID +ShowMembersInGroupDir +TeleportFlags +AssetData +SlaveParcelData +MultipleObjectUpdate +ObjectUpdate +ImprovedTerseObjectUpdate +ConfirmXferPacket +StartPingCheck +SimWideDeletes +UserListReply +IsPhantom +AgentList +RezObject +ClaimDate +MergeParcel +Priority +Building +QueryText +ReturnType +FetchFolders +SimulatorPublicHostBlock +HeaderData +GroupBlock +RequestMultipleObjects +RetrieveInstantMessages +DequeueInstantMessages +OpenCircuit +SecureSessionID +CrossedRegion +DirGroupsReply +AvatarsGroupReply +EmailMessageReply +GroupVoteHistoryItemReply +ViewerPosition +Position +ParentEstate +MuteName +StartParcelRename +BulkParcelRename +ParcelRename +ViewerFilename +Positive +UserReportInternal +AvatarPropertiesRequest +ParcelPropertiesRequest +GroupPropertiesRequest +GroupProfileRequest +AgentDataUpdateRequest +PriceObjectScaleFactor +DirPicksQuery +OpenEnrollment +GroupData +PauseBlock +RequestGodlikePowers +GrantGodlikePowers +TransactionID +DestinationID +Controls +FirstDetachAll +EstateID +ImprovedInstantMessage +AgentQuit +WantToFlags +CheckParcelSales +ParcelSales +CurrentInterval +PriceRentLight +MediaAutoScale +PriceRentLight +NeighborBlock +LayerData +NVPairData +TeleportLocal +LayersPaused +VoteInitiator +MailPingBounce +TypeData +SystemKickUser +ErrorCode +SLXML_ID +TransactionTime +TimeToLive +StartParcelRemove +BulkParcelRemove +DirGroupsQuery +BonusEstimate +MusicURL +CompleteLure +EjectUser +CoarseLocationUpdate +ChildAgentPositionUpdate +GroupIndex +GroupName +PriceParcelRent +SimStatus +TransactionSuccess +LureType +GroupMask +SitObject +AssetNum +Override +LocomotionState +PriceUpload +RemoveParcel +ConfirmAuctionStart +RpcScriptRequestInbound +SimWideMaxObjects +SimWideTotalObjects +TotalObjects +GroupObjects +ParcelReturnObjects +Questions +TransferAbort +TransferInventory +Collada_ID +RayTargetID +ClaimPrice +ObjectProperties +ParcelProperties +LogoutRequest +AssetUploadRequest +ReputationIndividualRequest +MajorVersion +MinorVersion +SimulatorAssign +TransactionType +AvatarPropertiesUpdate +ParcelPropertiesUpdate +FetchItems +AbortXfer +DeRezAck +TakeControls +DirLandReply +SpaceLocationTeleportReply +IMViaEMail +StartExpungeProcessAck +RentPrice +ChildAgentAlive +SpawnPointBlock +AttachmentBlock +RecallData +OfficerData +GroupOfficer +ObjectMaterial +AvatarNotesReply +CacheID +OwnerMask +TransferInventoryAck \ No newline at end of file diff --git a/include/Decoder.h b/include/Decoder.h new file mode 100644 index 00000000..292bb78d --- /dev/null +++ b/include/Decoder.h @@ -0,0 +1,15 @@ +#ifndef _SL_DECODER_ +#define _SL_DECODER_ + +#include "includes.h" + +class Decoder +{ +protected: + +public: + Decoder(); + virtual ~Decoder(); +}; + +#endif //_SL_DECODER_ diff --git a/include/Network.h b/include/Network.h new file mode 100644 index 00000000..fc61ba42 --- /dev/null +++ b/include/Network.h @@ -0,0 +1,33 @@ +#ifndef _SL_NETWORK_ +#define _SL_NETWORK_ + +#include "includes.h" +#include "Packet.h" +#include "SimConnection.h" + +class LIBSECONDLIFE_CLASS_DECL Network +{ +protected: + boost::asio::demuxer _demuxer; + SimConnection* _currentSim; + std::vector _connections; + std::list _inbox; + ProtocolManager* _protocol; + + LLUUID _avatar_id; + LLUUID _session_id; + LLUUID _secure_session_id; + +public: + boost::mutex _inboxMutex; + + //Network(); + Network(ProtocolManager* protocol); + virtual ~Network(); + + int connectSim(boost::asio::ipv4::address ip, unsigned short port, U32 code, bool setCurrent = false); + int sendPacket(boost::asio::ipv4::address ip, unsigned short port, Packet* packet); + void receivePacket(const boost::asio::error& error, std::size_t length, char* receiveBuffer); +}; + +#endif //_SL_NETWORK_ diff --git a/include/Packet.h b/include/Packet.h new file mode 100644 index 00000000..8c695b83 --- /dev/null +++ b/include/Packet.h @@ -0,0 +1,39 @@ +#ifndef _SL_PACKET_ +#define _SL_PACKET_ + +#include "includes.h" +#include "ProtocolManager.h" + +// Higher value will mean less realloc()s, more wasted memory. Lower value is +// vice versa. +#define DEFAULT_PACKET_SIZE 128 + +class LIBSECONDLIFE_CLASS_DECL Packet +{ +protected: + packetDiagram* _layout; + byte* _buffer; + size_t _length; + boost::asio::ipv4::udp::endpoint _remoteHost; + ProtocolManager* _protocol; + +public: + Packet(); + Packet(ProtocolManager* protocol, size_t length = 0); + virtual ~Packet(); + + bool setCommand(std::string command); + ll::llType getFieldType(std::string block, std::string field); + void* getField(std::string block, size_t blockNumber, std::string field); + int setField(std::string block, size_t blockNumber, std::string field, void* value); + + size_t getLength() { return _length; }; + int getRawData(byte* buffer, size_t length); + byte* getRawDataPtr(); + void setRawData(byte* buffer, size_t length); + + boost::asio::ipv4::udp::endpoint getRemoteHost(); + void setRemoteHost(boost::asio::ipv4::udp::endpoint remoteHost); +}; + +#endif //_SL_PACKET_ diff --git a/include/ProtocolManager.h b/include/ProtocolManager.h new file mode 100644 index 00000000..06f9fb5c --- /dev/null +++ b/include/ProtocolManager.h @@ -0,0 +1,128 @@ +#ifndef _SL_PROTOCOLMANAGER_ +#define _SL_PROTOCOLMANAGER_ + +#include "includes.h" + +namespace ll +{ + enum llType { + INVALID_TYPE = -1, + U8, + U16, + U32, + U64, + S8, + S16, + S32, + S64, + F8, + F16, + F32, + F64, + LLUUID, + BOOL, + LLVector3, + LLVector3d, + LLQuaternion, + IPADDR, + IPPORT, + Variable, + Fixed, + Single, + Multiple + }; +}; + +typedef struct llVector3 { + float x; + float y; + float z; +} llVector3; + +typedef struct llVector3d { + double x; + double y; + double z; +} llVector3d; + +typedef struct llQuaternion { + float x; + float y; + float z; + float s; +} llQuaternion; + +struct packetField { + int keywordPosition; + std::string name; + ll::llType type; +}; + +struct packetBlock { + int keywordPosition; + std::string name; + int frequency; + std::list fields; +}; + +namespace std +{ + template<> struct greater { + bool operator()(packetField const* p1, packetField const* p2) { + if(!p1) return true; + if(!p2) return false; + return p1->keywordPosition < p2->keywordPosition; + } + }; + + template<> struct greater { + bool operator()(packetBlock const* p1, packetBlock const* p2) { + if(!p1) return true; + if(!p2) return false; + return p1->keywordPosition < p2->keywordPosition; + } + }; +}; + +typedef struct packetDiagram { + std::string name; + bool trusted; + bool encoded; + std::list blocks; +} packetDiagram; + + +class ProtocolManager +{ +protected: + std::map _keywordMap; + + // At some point these should become maps from command names to packetDiagram*s + packetDiagram _lowPackets[65536]; + packetDiagram _mediumPackets[256]; + packetDiagram _highPackets[256]; + + bool getFields(packetBlock* block, std::string protocolMap, size_t start, size_t end); + bool getBlocks(packetDiagram* packet, std::string protocolMap, size_t start, size_t end); +public: + ProtocolManager(); + virtual ~ProtocolManager(); + + void printMap(); + + int loadKeywords(std::string filename); + int decryptCommFile(std::string source, std::string destination); + int buildProtocolMap(std::string filename); + + int getKeywordPosition(std::string keyword); + + packetDiagram* getCommand(std::string command); + ll::llType getFieldType(std::string type); + static int getTypeSize(ll::llType type); + std::string getTypeName(ll::llType type); + int getBlockFrequency(packetDiagram* layout, std::string block); + size_t getBlockSize(packetDiagram* layout, std::string block); + int getFieldOffset(packetDiagram* layout, std::string block, std::string field); +}; + +#endif //_SL_PROTOCOLMANAGER_ diff --git a/include/SecondLife.h b/include/SecondLife.h new file mode 100644 index 00000000..1f32c3f0 --- /dev/null +++ b/include/SecondLife.h @@ -0,0 +1,26 @@ +#ifndef _SL_SECONDLIFE_ +#define _SL_SECONDLIFE_ + +#include "includes.h" +#include "ProtocolManager.h" +#include "Network.h" +#include "Decoder.h" + +class LIBSECONDLIFE_CLASS_DECL SecondLife +{ +//protected: +public: + ProtocolManager* _protocol; + Network* _network; + Decoder* _decoder; +//public: + SecondLife(); + virtual ~SecondLife(); + + // Pass-through functions to make life easier on the client + int loadKeywords(std::string filename) { return _protocol->loadKeywords(filename); }; + int decryptCommFile(std::string source, std::string destination) { return _protocol->decryptCommFile(source, destination); }; + int buildProtocolMap(std::string filename) { return _protocol->buildProtocolMap(filename); }; +}; + +#endif //_SL_SECONDLIFE_ diff --git a/include/SimConnection.h b/include/SimConnection.h new file mode 100644 index 00000000..199ddf91 --- /dev/null +++ b/include/SimConnection.h @@ -0,0 +1,43 @@ +#ifndef _SL_SIMCONNECTION_ +#define _SL_SIMCONNECTION_ + +#include "includes.h" + +class SimConnection +{ +protected: + std::string _name; + U32 _code; + boost::asio::ipv4::udp::endpoint _endpoint; + boost::asio::datagram_socket* _socket; + bool _running; + char* _buffer; + +public: + SimConnection(); + SimConnection(boost::asio::ipv4::address ip, unsigned short port, U32 code); + virtual ~SimConnection(); + + boost::asio::ipv4::udp::endpoint endpoint() { return _endpoint; }; + void endpoint(boost::asio::ipv4::udp::endpoint endpoint) { _endpoint = endpoint; }; + + boost::asio::datagram_socket* socket() { return _socket; }; + void socket(boost::asio::datagram_socket* socket) { _socket = socket; }; + + boost::asio::ipv4::address ip() { return _endpoint.address(); }; + void ip(boost::asio::ipv4::address ip) { _endpoint.address(ip); }; + + unsigned short port() { return _endpoint.port(); }; + void port(unsigned short port) { return _endpoint.port(port); }; + + bool running() { return _running; }; + void running(bool running) { _running = running; }; + + char* buffer() { return _buffer; }; + size_t bufferSize() { return SL_BUFFER_SIZE; }; + + bool operator==(SimConnection &p); + bool operator!=(SimConnection &p); +}; + +#endif //_SL_SIMCONNECTION_ diff --git a/include/boost/asio.hpp b/include/boost/asio.hpp new file mode 100644 index 00000000..57704957 --- /dev/null +++ b/include/boost/asio.hpp @@ -0,0 +1,64 @@ +// +// asio.hpp +// ~~~~~~~~ +// +// Copyright (c) 2003-2005 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_HPP +#define BOOST_ASIO_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#endif // BOOST_ASIO_HPP diff --git a/include/boost/asio/basic_datagram_socket.hpp b/include/boost/asio/basic_datagram_socket.hpp new file mode 100644 index 00000000..31a6d92c --- /dev/null +++ b/include/boost/asio/basic_datagram_socket.hpp @@ -0,0 +1,1295 @@ +// +// basic_datagram_socket.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2005 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_BASIC_DATAGRAM_SOCKET_HPP +#define BOOST_ASIO_BASIC_DATAGRAM_SOCKET_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +namespace boost { +namespace asio { + +/// Provides datagram-oriented socket functionality. +/** + * The basic_datagram_socket class template provides asynchronous and blocking + * datagram-oriented socket functionality. + * + * Most applications will use the boost::asio::datagram_socket typedef. + * + * @par Thread Safety: + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Unsafe. + * + * @par Concepts: + * Async_Object, Error_Source. + */ +template +class basic_datagram_socket + : public socket_base, + private noncopyable +{ +public: + /// The type of the service that will be used to provide socket operations. + typedef Service service_type; + + /// The native implementation type of the datagram socket. + typedef typename service_type::impl_type impl_type; + + /// The demuxer type for this asynchronous type. + typedef typename service_type::demuxer_type demuxer_type; + + /// The type used for reporting errors. + typedef boost::asio::error error_type; + + /// A basic_datagram_socket is always the lowest layer. + typedef basic_datagram_socket lowest_layer_type; + + /// Construct a basic_datagram_socket without opening it. + /** + * This constructor creates a datagram socket without opening it. The open() + * function must be called before data can be sent or received on the socket. + * + * @param d The demuxer object that the datagram socket will use to dispatch + * handlers for any asynchronous operations performed on the socket. + */ + explicit basic_datagram_socket(demuxer_type& d) + : service_(d.get_service(service_factory())), + impl_(service_.null()) + { + } + + /// Construct a basic_datagram_socket, opening it and binding it to the given + /// local endpoint. + /** + * This constructor creates a datagram socket and automatically opens it bound + * to the specified endpoint on the local machine. The protocol used is the + * protocol associated with the given endpoint. + * + * @param d The demuxer object that the datagram socket will use to dispatch + * handlers for any asynchronous operations performed on the socket. + * + * @param endpoint An endpoint on the local machine to which the datagram + * socket will be bound. + * + * @throws boost::asio::error Thrown on failure. + */ + template + basic_datagram_socket(demuxer_type& d, const Endpoint& endpoint) + : service_(d.get_service(service_factory())), + impl_(service_.null()) + { + service_.open(impl_, endpoint.protocol(), throw_error()); + close_on_block_exit auto_close(service_, impl_); + service_.bind(impl_, endpoint, throw_error()); + auto_close.cancel(); + } + + /// Destructor. + ~basic_datagram_socket() + { + service_.close(impl_, ignore_error()); + } + + /// Get the demuxer associated with the asynchronous object. + /** + * This function may be used to obtain the demuxer object that the datagram + * socket uses to dispatch handlers for asynchronous operations. + * + * @return A reference to the demuxer object that datagram socket will use to + * dispatch handlers. Ownership is not transferred to the caller. + */ + demuxer_type& demuxer() + { + return service_.demuxer(); + } + + /// Open the socket using the specified protocol. + /** + * This function opens the datagram socket so that it will use the specified + * protocol. + * + * @param protocol An object specifying which protocol is to be used. + * + * @throws boost::asio::error Thrown on failure. + * + * @par Example: + * @code + * boost::asio::datagram_socket socket(demuxer); + * socket.open(boost::asio::ipv4::udp()); + * @endcode + */ + template + void open(const Protocol& protocol) + { + service_.open(impl_, protocol, throw_error()); + } + + /// Open the socket using the specified protocol. + /** + * This function opens the datagram socket so that it will use the specified + * protocol. + * + * @param protocol An object specifying which protocol is to be used. + * + * @param error_handler The handler to be called when an error occurs. Copies + * will be made of the handler as required. The function signature of the + * handler must be: + * @code void error_handler( + * const boost::asio::error& error // Result of operation + * ); @endcode + * + * @par Example: + * @code + * boost::asio::datagram_socket socket(demuxer); + * boost::asio::error error; + * socket.open(boost::asio::ipv4::udp(), boost::asio::assign_error(error)); + * if (error) + * { + * // An error occurred. + * } + * @endcode + */ + template + void open(const Protocol& protocol, Error_Handler error_handler) + { + service_.open(impl_, protocol, error_handler); + } + + /// Close the socket. + /** + * This function is used to close the datagram socket. Any asynchronous send + * or receive operations will be cancelled immediately. + * + * A subsequent call to open() is required before the socket can again be + * used to again perform send and receive operations. + * + * @throws boost::asio::error Thrown on failure. + */ + void close() + { + service_.close(impl_, throw_error()); + } + + /// Close the socket. + /** + * This function is used to close the datagram socket. Any asynchronous send + * or receive operations will be cancelled immediately. + * + * A subsequent call to open() is required before the socket can again be + * used to again perform send and receive operations. + * + * @param error_handler The handler to be called when an error occurs. Copies + * will be made of the handler as required. The function signature of the + * handler must be: + * @code void error_handler( + * const boost::asio::error& error // Result of operation + * ); @endcode + * + * @par Example: + * @code + * boost::asio::datagram_socket socket(demuxer); + * ... + * boost::asio::error error; + * socket.close(boost::asio::assign_error(error)); + * if (error) + * { + * // An error occurred. + * } + * @endcode + */ + template + void close(Error_Handler error_handler) + { + service_.close(impl_, error_handler); + } + + /// Get a reference to the lowest layer. + /** + * This function returns a reference to the lowest layer in a stack of + * layers. Since a basic_datagram_socket cannot contain any further layers, + * it simply returns a reference to itself. + * + * @return A reference to the lowest layer in the stack of layers. Ownership + * is not transferred to the caller. + */ + lowest_layer_type& lowest_layer() + { + return *this; + } + + /// Get the underlying implementation in the native type. + /** + * This function may be used to obtain the underlying implementation of the + * datagram socket. This is intended to allow access to native socket + * functionality that is not otherwise provided. + */ + impl_type impl() + { + return impl_; + } + + /// Set the underlying implementation in the native type. + /** + * This function is used by the acceptor implementation to set the underlying + * implementation associated with the datagram socket. + * + * @param new_impl The new underlying socket implementation. + */ + void set_impl(impl_type new_impl) + { + service_.assign(impl_, new_impl); + } + + /// Bind the socket to the given local endpoint. + /** + * This function binds the datagram socket to the specified endpoint on the + * local machine. + * + * @param endpoint An endpoint on the local machine to which the datagram + * socket will be bound. + * + * @throws boost::asio::error Thrown on failure. + * + * @par Example: + * @code + * boost::asio::datagram_socket socket(demuxer); + * socket.open(boost::asio::ipv4::udp()); + * socket.bind(boost::asio::ipv4::udp::endpoint(12345)); + * @endcode + */ + template + void bind(const Endpoint& endpoint) + { + service_.bind(impl_, endpoint, throw_error()); + } + + /// Bind the socket to the given local endpoint. + /** + * This function binds the datagram socket to the specified endpoint on the + * local machine. + * + * @param endpoint An endpoint on the local machine to which the datagram + * socket will be bound. + * + * @param error_handler The handler to be called when an error occurs. Copies + * will be made of the handler as required. The function signature of the + * handler must be: + * @code void error_handler( + * const boost::asio::error& error // Result of operation + * ); @endcode + * + * @par Example: + * @code + * boost::asio::datagram_socket socket(demuxer); + * socket.open(boost::asio::ipv4::udp()); + * boost::asio::error error; + * socket.bind(boost::asio::ipv4::udp::endpoint(12345), + * boost::asio::assign_error(error)); + * if (error) + * { + * // An error occurred. + * } + * @endcode + */ + template + void bind(const Endpoint& endpoint, Error_Handler error_handler) + { + service_.bind(impl_, endpoint, error_handler); + } + + /// Connect a datagram socket to the specified endpoint. + /** + * This function is used to connect a datagram socket to the specified remote + * endpoint. The function call will block until the connection is successfully + * made or an error occurs. + * + * The socket is automatically opened if it is not already open. If the + * connect fails, and the socket was automatically opened, the socket is + * returned to the closed state. + * + * @param peer_endpoint The remote endpoint to which the socket will be + * connected. + * + * @throws boost::asio::error Thrown on failure. + * + * @par Example: + * @code + * boost::asio::datagram_socket socket(demuxer); + * ... + * boost::asio::ipv4::udp::endpoint endpoint(12345, "1.2.3.4"); + * socket.connect(endpoint); + * @endcode + */ + template + void connect(const Endpoint& peer_endpoint) + { + service_.connect(impl_, peer_endpoint, throw_error()); + } + + /// Connect a datagram socket to the specified endpoint. + /** + * This function is used to connect a datagram socket to the specified remote + * endpoint. The function call will block until the connection is successfully + * made or an error occurs. + * + * The socket is automatically opened if it is not already open. If the + * connect fails, and the socket was automatically opened, the socket is + * returned to the closed state. + * + * @param peer_endpoint The remote endpoint to which the socket will be + * connected. + * + * @param error_handler The handler to be called when an error occurs. Copies + * will be made of the handler as required. The function signature of the + * handler must be: + * @code void error_handler( + * const boost::asio::error& error // Result of operation + * ); @endcode + * + * @par Example: + * @code + * boost::asio::datagram_socket socket(demuxer); + * ... + * boost::asio::ipv4::udp::endpoint endpoint(12345, "1.2.3.4"); + * boost::asio::error error; + * socket.connect(endpoint, boost::asio::assign_error(error)); + * if (error) + * { + * // An error occurred. + * } + * @endcode + */ + template + void connect(const Endpoint& peer_endpoint, Error_Handler error_handler) + { + service_.connect(impl_, peer_endpoint, error_handler); + } + + /// Start an asynchronous connect. + /** + * This function is used to asynchronously connect a datagram socket to the + * specified remote endpoint. The function call always returns immediately. + * + * The socket is automatically opened if it is not already open. If the + * connect fails, and the socket was automatically opened, the socket is + * returned to the closed state. + * + * @param peer_endpoint The remote endpoint to which the socket will be + * connected. Copies will be made of the endpoint object as required. + * + * @param handler The handler to be called when the connection operation + * completes. Copies will be made of the handler as required. The function + * signature of the handler must be: + * @code void handler( + * const boost::asio::error& error // Result of operation + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * boost::asio::demuxer::post(). + * + * @par Example: + * @code + * void connect_handler(const boost::asio::error& error) + * { + * if (!error) + * { + * // Connect succeeded. + * } + * } + * + * ... + * + * boost::asio::datagram_socket socket(demuxer); + * ... + * boost::asio::ipv4::udp::endpoint endpoint(12345, "1.2.3.4"); + * socket.async_connect(endpoint, connect_handler); + * @endcode + */ + template + void async_connect(const Endpoint& peer_endpoint, Handler handler) + { + service_.async_connect(impl_, peer_endpoint, handler); + } + + /// Set an option on the socket. + /** + * This function is used to set an option on the socket. + * + * @param option The new option value to be set on the socket. + * + * @throws boost::asio::error Thrown on failure. + * + * @sa Socket_Option @n + * boost::asio::socket_base::broadcast @n + * boost::asio::socket_base::do_not_route @n + * boost::asio::socket_base::reuse_address @n + * boost::asio::ipv4::multicast::add_membership @n + * boost::asio::ipv4::multicast::drop_membership @n + * boost::asio::ipv4::multicast::outbound_interface @n + * boost::asio::ipv4::multicast::time_to_live @n + * boost::asio::ipv4::multicast::enable_loopback + * + * @par Example: + * Setting the SOL_SOCKET/SO_DONTROUTE option. + * @code + * boost::asio::datagram_socket socket(demuxer); + * ... + * boost::asio::datagram_socket::do_not_route option(true); + * socket.set_option(option); + * @endcode + */ + template + void set_option(const Socket_Option& option) + { + service_.set_option(impl_, option, throw_error()); + } + + /// Set an option on the socket. + /** + * This function is used to set an option on the socket. + * + * @param option The new option value to be set on the socket. + * + * @param error_handler The handler to be called when an error occurs. Copies + * will be made of the handler as required. The function signature of the + * handler must be: + * @code void error_handler( + * const boost::asio::error& error // Result of operation + * ); @endcode + * + * @sa Socket_Option @n + * boost::asio::socket_base::broadcast @n + * boost::asio::socket_base::do_not_route @n + * boost::asio::socket_base::reuse_address @n + * boost::asio::ipv4::multicast::add_membership @n + * boost::asio::ipv4::multicast::drop_membership @n + * boost::asio::ipv4::multicast::outbound_interface @n + * boost::asio::ipv4::multicast::time_to_live @n + * boost::asio::ipv4::multicast::enable_loopback + * + * @par Example: + * Setting the SOL_SOCKET/SO_DONTROUTE option. + * @code + * boost::asio::datagram_socket socket(demuxer); + * ... + * boost::asio::datagram_socket::do_not_route option(true); + * boost::asio::error error; + * socket.set_option(option, boost::asio::assign_error(error)); + * if (error) + * { + * // An error occurred. + * } + * @endcode + */ + template + void set_option(const Socket_Option& option, Error_Handler error_handler) + { + service_.set_option(impl_, option, error_handler); + } + + /// Get an option from the socket. + /** + * This function is used to get the current value of an option on the socket. + * + * @param option The option value to be obtained from the socket. + * + * @throws boost::asio::error Thrown on failure. + * + * @sa Socket_Option @n + * boost::asio::socket_base::broadcast @n + * boost::asio::socket_base::do_not_route @n + * boost::asio::socket_base::reuse_address @n + * boost::asio::ipv4::multicast::add_membership @n + * boost::asio::ipv4::multicast::drop_membership @n + * boost::asio::ipv4::multicast::outbound_interface @n + * boost::asio::ipv4::multicast::time_to_live @n + * boost::asio::ipv4::multicast::enable_loopback + * + * @par Example: + * Getting the value of the SOL_SOCKET/SO_DONTROUTE option. + * @code + * boost::asio::datagram_socket socket(demuxer); + * ... + * boost::asio::datagram_socket::do_not_route option(true); + * socket.get_option(option); + * bool is_set = option.get(); + * @endcode + */ + template + void get_option(Socket_Option& option) const + { + service_.get_option(impl_, option, throw_error()); + } + + /// Get an option from the socket. + /** + * This function is used to get the current value of an option on the socket. + * + * @param option The option value to be obtained from the socket. + * + * @param error_handler The handler to be called when an error occurs. Copies + * will be made of the handler as required. The function signature of the + * handler must be: + * @code void error_handler( + * const boost::asio::error& error // Result of operation + * ); @endcode + * + * @sa Socket_Option @n + * boost::asio::socket_base::broadcast @n + * boost::asio::socket_base::do_not_route @n + * boost::asio::socket_base::reuse_address @n + * boost::asio::ipv4::multicast::add_membership @n + * boost::asio::ipv4::multicast::drop_membership @n + * boost::asio::ipv4::multicast::outbound_interface @n + * boost::asio::ipv4::multicast::time_to_live @n + * boost::asio::ipv4::multicast::enable_loopback + * + * @par Example: + * Getting the value of the SOL_SOCKET/SO_DONTROUTE option. + * @code + * boost::asio::datagram_socket socket(demuxer); + * ... + * boost::asio::datagram_socket::do_not_route option(true); + * boost::asio::error error; + * socket.get_option(option, boost::asio::assign_error(error)); + * if (error) + * { + * // An error occurred. + * } + * bool is_set = option.get(); + * @endcode + */ + template + void get_option(Socket_Option& option, Error_Handler error_handler) const + { + service_.get_option(impl_, option, error_handler); + } + + /// Perform an IO control command on the socket. + /** + * This function is used to execute an IO control command on the socket. + * + * @param command The IO control command to be performed on the socket. + * + * @throws boost::asio::error Thrown on failure. + * + * @sa IO_Control_Command @n + * boost::asio::socket_base::bytes_readable @n + * boost::asio::socket_base::non_blocking_io + * + * @par Example: + * Getting the number of bytes ready to read: + * @code + * boost::asio::datagram_socket socket(demuxer); + * ... + * boost::asio::datagram_socket::bytes_readable command; + * socket.io_control(command); + * std::size_t bytes_readable = command.get(); + * @endcode + */ + template + void io_control(IO_Control_Command& command) + { + service_.io_control(impl_, command, throw_error()); + } + + /// Perform an IO control command on the socket. + /** + * This function is used to execute an IO control command on the socket. + * + * @param command The IO control command to be performed on the socket. + * + * @param error_handler The handler to be called when an error occurs. Copies + * will be made of the handler as required. The function signature of the + * handler must be: + * @code void error_handler( + * const boost::asio::error& error // Result of operation + * ); @endcode + * + * @sa IO_Control_Command @n + * boost::asio::socket_base::bytes_readable @n + * boost::asio::socket_base::non_blocking_io + * + * @par Example: + * Getting the number of bytes ready to read: + * @code + * boost::asio::datagram_socket socket(demuxer); + * ... + * boost::asio::datagram_socket::bytes_readable command; + * boost::asio::error error; + * socket.io_control(command, boost::asio::assign_error(error)); + * if (error) + * { + * // An error occurred. + * } + * std::size_t bytes_readable = command.get(); + * @endcode + */ + template + void io_control(IO_Control_Command& command, Error_Handler error_handler) + { + service_.io_control(impl_, command, error_handler); + } + + /// Get the local endpoint of the socket. + /** + * This function is used to obtain the locally bound endpoint of the socket. + * + * @param endpoint An endpoint object that receives the local endpoint of the + * socket. + * + * @throws boost::asio::error Thrown on failure. + * + * @par Example: + * @code + * boost::asio::datagram_socket socket(demuxer); + * ... + * boost::asio::ipv4::udp::endpoint endpoint; + * socket.get_local_endpoint(endpoint); + * @endcode + */ + template + void get_local_endpoint(Endpoint& endpoint) const + { + service_.get_local_endpoint(impl_, endpoint, throw_error()); + } + + /// Get the local endpoint of the socket. + /** + * This function is used to obtain the locally bound endpoint of the socket. + * + * @param endpoint An endpoint object that receives the local endpoint of the + * socket. + * + * @param error_handler The handler to be called when an error occurs. Copies + * will be made of the handler as required. The function signature of the + * handler must be: + * @code void error_handler( + * const boost::asio::error& error // Result of operation + * ); @endcode + * + * @par Example: + * @code + * boost::asio::datagram_socket socket(demuxer); + * ... + * boost::asio::ipv4::udp::endpoint endpoint; + * boost::asio::error error; + * socket.get_local_endpoint(endpoint, boost::asio::assign_error(error)); + * if (error) + * { + * // An error occurred. + * } + * @endcode + */ + template + void get_local_endpoint(Endpoint& endpoint, + Error_Handler error_handler) const + { + service_.get_local_endpoint(impl_, endpoint, error_handler); + } + + /// Get the remote endpoint of the socket. + /** + * This function is used to obtain the remote endpoint of the socket. + * + * @param endpoint An endpoint object that receives the remote endpoint of + * the socket. + * + * @throws boost::asio::error Thrown on failure. + * + * @par Example: + * @code + * boost::asio::datagram_socket socket(demuxer); + * ... + * boost::asio::ipv4::udp::endpoint endpoint; + * socket.get_remote_endpoint(endpoint); + * @endcode + */ + template + void get_remote_endpoint(Endpoint& endpoint) const + { + service_.get_remote_endpoint(impl_, endpoint, throw_error()); + } + + /// Get the remote endpoint of the socket. + /** + * This function is used to obtain the remote endpoint of the socket. + * + * @param endpoint An endpoint object that receives the remote endpoint of + * the socket. + * + * @param error_handler The handler to be called when an error occurs. Copies + * will be made of the handler as required. The function signature of the + * handler must be: + * @code void error_handler( + * const boost::asio::error& error // Result of operation + * ); @endcode + * + * @par Example: + * @code + * boost::asio::datagram_socket socket(demuxer); + * ... + * boost::asio::ipv4::udp::endpoint endpoint; + * boost::asio::error error; + * socket.get_remote_endpoint(endpoint, boost::asio::assign_error(error)); + * if (error) + * { + * // An error occurred. + * } + * @endcode + */ + template + void get_remote_endpoint(Endpoint& endpoint, + Error_Handler error_handler) const + { + service_.get_remote_endpoint(impl_, endpoint, error_handler); + } + + /// Disable sends or receives on the socket. + /** + * This function is used to disable send operations, receive operations, or + * both. + * + * @param what Determines what types of operation will no longer be allowed. + * + * @throws boost::asio::error Thrown on failure. + * + * @par Example: + * Shutting down the send side of the socket: + * @code + * boost::asio::datagram_socket socket(demuxer); + * ... + * socket.shutdown(boost::asio::datagram_socket::shutdown_send); + * @endcode + */ + void shutdown(shutdown_type what) + { + service_.shutdown(impl_, what, throw_error()); + } + + /// Disable sends or receives on the socket. + /** + * This function is used to disable send operations, receive operations, or + * both. + * + * @param what Determines what types of operation will no longer be allowed. + * + * @param error_handler The handler to be called when an error occurs. Copies + * will be made of the handler as required. The function signature of the + * handler must be: + * @code void error_handler( + * const boost::asio::error& error // Result of operation + * ); @endcode + * + * @par Example: + * Shutting down the send side of the socket: + * @code + * boost::asio::datagram_socket socket(demuxer); + * ... + * boost::asio::error error; + * socket.shutdown(boost::asio::datagram_socket::shutdown_send, + * boost::asio::assign_error(error)); + * if (error) + * { + * // An error occurred. + * } + * @endcode + */ + template + void shutdown(shutdown_type what, Error_Handler error_handler) + { + service_.shutdown(impl_, what, error_handler); + } + + /// Send some data on a connected socket. + /** + * This function is used to send data on the datagram socket. The function + * call will block until the data has been sent successfully or an error + * occurs. + * + * @param buffers One ore more data buffers to be sent on the socket. + * + * @param flags Flags specifying how the send call is to be made. + * + * @returns The number of bytes sent. + * + * @throws boost::asio::error Thrown on failure. + * + * @note The send operation can only be used with a connected socket. Use + * the send_to function to send data on an unconnected datagram socket. + * + * @par Example: + * To send a single data buffer use the @ref buffer function as follows: + * @code socket.send(boost::asio::buffer(data, size), 0); @endcode + * See the @ref buffer documentation for information on sending multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + std::size_t send(const Const_Buffers& buffers, message_flags flags) + { + return service_.send(impl_, buffers, flags, throw_error()); + } + + /// Send some data on a connected socket. + /** + * This function is used to send data on the datagram socket. The function + * call will block until the data has been sent successfully or an error + * occurs. + * + * @param buffers One or more data buffers to be sent on the socket. + * + * @param flags Flags specifying how the send call is to be made. + * + * @param error_handler The handler to be called when an error occurs. Copies + * will be made of the handler as required. The function signature of the + * handler must be: + * @code void error_handler( + * const boost::asio::error& error // Result of operation + * ); @endcode + * + * @returns The number of bytes sent. + * + * @note The send operation can only be used with a connected socket. Use + * the send_to function to send data on an unconnected datagram socket. + */ + template + std::size_t send(const Const_Buffers& buffers, message_flags flags, + Error_Handler error_handler) + { + return service_.send(impl_, buffers, flags, error_handler); + } + + /// Start an asynchronous send on a connected socket. + /** + * This function is used to send data on the datagram socket. The function + * call will block until the data has been sent successfully or an error + * occurs. + * + * @param buffers One or more data buffers to be sent on the socket. Although + * the buffers object may be copied as necessary, ownership of the underlying + * memory blocks is retained by the caller, which must guarantee that they + * remain valid until the handler is called. + * + * @param flags Flags specifying how the send call is to be made. + * + * @param handler The handler to be called when the send operation completes. + * Copies will be made of the handler as required. The function signature of + * the handler must be: + * @code void handler( + * const boost::asio::error& error, // Result of operation + * std::size_t bytes_transferred // Number of bytes sent + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * boost::asio::demuxer::post(). + * + * @note The async_send operation can only be used with a connected socket. + * Use the async_send_to function to send data on an unconnected datagram + * socket. + * + * @par Example: + * To send a single data buffer use the @ref buffer function as follows: + * @code + * socket.async_send(boost::asio::buffer(data, size), 0, handler); + * @endcode + * See the @ref buffer documentation for information on sending multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + void async_send(const Const_Buffers& buffers, message_flags flags, + Handler handler) + { + service_.async_send(impl_, buffers, flags, handler); + } + + /// Send a datagram to the specified endpoint. + /** + * This function is used to send a datagram to the specified remote endpoint. + * The function call will block until the data has been sent successfully or + * an error occurs. + * + * @param buffers One or more data buffers to be sent to the remote endpoint. + * + * @param flags Flags specifying how the send call is to be made. + * + * @param destination The remote endpoint to which the data will be sent. + * + * @returns The number of bytes sent. + * + * @throws boost::asio::error Thrown on failure. + * + * @par Example: + * To send a single data buffer use the @ref buffer function as follows: + * @code + * boost::asio::ipv4::udp::endpoint destination(12345, "1.2.3.4"); + * socket.send_to(boost::asio::buffer(data, size), 0, destination); + * @endcode + * See the @ref buffer documentation for information on sending multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + std::size_t send_to(const Const_Buffers& buffers, message_flags flags, + const Endpoint& destination) + { + return service_.send_to(impl_, buffers, flags, destination, throw_error()); + } + + /// Send a datagram to the specified endpoint. + /** + * This function is used to send a datagram to the specified remote endpoint. + * The function call will block until the data has been sent successfully or + * an error occurs. + * + * @param buffers One or more data buffers to be sent to the remote endpoint. + * + * @param flags Flags specifying how the send call is to be made. + * + * @param destination The remote endpoint to which the data will be sent. + * + * @param error_handler The handler to be called when an error occurs. Copies + * will be made of the handler as required. The function signature of the + * handler must be: + * @code void error_handler( + * const boost::asio::error& error // Result of operation + * ); @endcode + * + * @returns The number of bytes sent. + */ + template + std::size_t send_to(const Const_Buffers& buffers, message_flags flags, + const Endpoint& destination, Error_Handler error_handler) + { + return service_.send_to(impl_, buffers, flags, destination, error_handler); + } + + /// Start an asynchronous send. + /** + * This function is used to asynchronously send a datagram to the specified + * remote endpoint. The function call always returns immediately. + * + * @param buffers One or more data buffers to be sent to the remote endpoint. + * Although the buffers object may be copied as necessary, ownership of the + * underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the handler is called. + * + * @param flags Flags specifying how the send call is to be made. + * + * @param destination The remote endpoint to which the data will be sent. + * Copies will be made of the endpoint as required. + * + * @param handler The handler to be called when the send operation completes. + * Copies will be made of the handler as required. The function signature of + * the handler must be: + * @code void handler( + * const boost::asio::error& error, // Result of operation + * std::size_t bytes_transferred // Number of bytes sent + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * boost::asio::demuxer::post(). + * + * @par Example: + * To send a single data buffer use the @ref buffer function as follows: + * @code + * boost::asio::ipv4::udp::endpoint destination(12345, "1.2.3.4"); + * socket.async_send_to( + * boost::asio::buffer(data, size), 0, destination, handler); + * @endcode + * See the @ref buffer documentation for information on sending multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + void async_send_to(const Const_Buffers& buffers, message_flags flags, + const Endpoint& destination, Handler handler) + { + service_.async_send_to(impl_, buffers, flags, destination, handler); + } + + /// Receive some data on a connected socket. + /** + * This function is used to receive data on the datagram socket. The function + * call will block until data has been received successfully or an error + * occurs. + * + * @param buffers One or more buffers into which the data will be received. + * + * @param flags Flags specifying how the receive call is to be made. + * + * @returns The number of bytes received. + * + * @throws boost::asio::error Thrown on failure. + * + * @note The receive operation can only be used with a connected socket. Use + * the receive_from function to receive data on an unconnected datagram + * socket. + * + * @par Example: + * To receive into a single data buffer use the @ref buffer function as + * follows: + * @code socket.receive(boost::asio::buffer(data, size), 0); @endcode + * See the @ref buffer documentation for information on receiving into + * multiple buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + std::size_t receive(const Mutable_Buffers& buffers, message_flags flags) + { + return service_.receive(impl_, buffers, flags, throw_error()); + } + + /// Receive some data on a connected socket. + /** + * This function is used to receive data on the datagram socket. The function + * call will block until data has been received successfully or an error + * occurs. + * + * @param buffers One or more buffers into which the data will be received. + * + * @param flags Flags specifying how the receive call is to be made. + * + * @param error_handler The handler to be called when an error occurs. Copies + * will be made of the handler as required. The function signature of the + * handler must be: + * @code void error_handler( + * const boost::asio::error& error // Result of operation + * ); @endcode + * + * @returns The number of bytes received. + * + * @note The receive operation can only be used with a connected socket. Use + * the receive_from function to receive data on an unconnected datagram + * socket. + */ + template + std::size_t receive(const Mutable_Buffers& buffers, message_flags flags, + Error_Handler error_handler) + { + return service_.receive(impl_, buffers, flags, error_handler); + } + + /// Start an asynchronous receive on a connected socket. + /** + * This function is used to asynchronously receive data from the datagram + * socket. The function call always returns immediately. + * + * @param buffers One or more buffers into which the data will be received. + * Although the buffers object may be copied as necessary, ownership of the + * underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the handler is called. + * + * @param flags Flags specifying how the receive call is to be made. + * + * @param handler The handler to be called when the receive operation + * completes. Copies will be made of the handler as required. The function + * signature of the handler must be: + * @code void handler( + * const boost::asio::error& error, // Result of operation + * std::size_t bytes_transferred // Number of bytes received + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * boost::asio::demuxer::post(). + * + * @note The async_receive operation can only be used with a connected socket. + * Use the async_receive_from function to receive data on an unconnected + * datagram socket. + * + * @par Example: + * To receive into a single data buffer use the @ref buffer function as + * follows: + * @code + * socket.async_receive(boost::asio::buffer(data, size), 0, handler); + * @endcode + * See the @ref buffer documentation for information on receiving into + * multiple buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + void async_receive(const Mutable_Buffers& buffers, message_flags flags, + Handler handler) + { + service_.async_receive(impl_, buffers, flags, handler); + } + + /// Receive a datagram with the endpoint of the sender. + /** + * This function is used to receive a datagram. The function call will block + * until data has been received successfully or an error occurs. + * + * @param buffers One or more buffers into which the data will be received. + * + * @param flags Flags specifying how the receive call is to be made. + * + * @param sender_endpoint An endpoint object that receives the endpoint of + * the remote sender of the datagram. + * + * @returns The number of bytes received. + * + * @throws boost::asio::error Thrown on failure. + * + * @par Example: + * To receive into a single data buffer use the @ref buffer function as + * follows: + * @code + * boost::asio::ipv4::udp::endpoint sender_endpoint; + * socket.receive_from( + * boost::asio::buffer(data, size), 0, sender_endpoint); + * @endcode + * See the @ref buffer documentation for information on receiving into + * multiple buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + std::size_t receive_from(const Mutable_Buffers& buffers, message_flags flags, + Endpoint& sender_endpoint) + { + return service_.receive_from(impl_, buffers, flags, sender_endpoint, + throw_error()); + } + + /// Receive a datagram with the endpoint of the sender. + /** + * This function is used to receive a datagram. The function call will block + * until data has been received successfully or an error occurs. + * + * @param buffers One or more buffers into which the data will be received. + * + * @param flags Flags specifying how the receive call is to be made. + * + * @param sender_endpoint An endpoint object that receives the endpoint of + * the remote sender of the datagram. + * + * @param error_handler The handler to be called when an error occurs. Copies + * will be made of the handler as required. The function signature of the + * handler must be: + * @code void error_handler( + * const boost::asio::error& error // Result of operation + * ); @endcode + * + * @returns The number of bytes received. + */ + template + std::size_t receive_from(const Mutable_Buffers& buffers, message_flags flags, + Endpoint& sender_endpoint, Error_Handler error_handler) + { + return service_.receive_from(impl_, buffers, flags, sender_endpoint, + error_handler); + } + + /// Start an asynchronous receive. + /** + * This function is used to asynchronously receive a datagram. The function + * call always returns immediately. + * + * @param buffers One or more buffers into which the data will be received. + * Although the buffers object may be copied as necessary, ownership of the + * underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the handler is called. + * + * @param flags Flags specifying how the receive call is to be made. + * + * @param sender_endpoint An endpoint object that receives the endpoint of + * the remote sender of the datagram. Ownership of the sender_endpoint object + * is retained by the caller, which must guarantee that it is valid until the + * handler is called. + * + * @param handler The handler to be called when the receive operation + * completes. Copies will be made of the handler as required. The function + * signature of the handler must be: + * @code void handler( + * const boost::asio::error& error, // Result of operation + * std::size_t bytes_transferred // Number of bytes received + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * boost::asio::demuxer::post(). + * + * @par Example: + * To receive into a single data buffer use the @ref buffer function as + * follows: + * @code socket.async_receive_from( + * boost::asio::buffer(data, size), 0, sender_endpoint, handler); @endcode + * See the @ref buffer documentation for information on receiving into + * multiple buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + void async_receive_from(const Mutable_Buffers& buffers, message_flags flags, + Endpoint& sender_endpoint, Handler handler) + { + service_.async_receive_from(impl_, buffers, flags, sender_endpoint, + handler); + } + +private: + /// The backend service implementation. + service_type& service_; + + /// The underlying native implementation. + impl_type impl_; + + // Helper class to automatically close the implementation on block exit. + class close_on_block_exit + { + public: + close_on_block_exit(service_type& service, impl_type& impl) + : service_(&service), impl_(impl) + { + } + + ~close_on_block_exit() + { + if (service_) + { + service_->close(impl_, ignore_error()); + } + } + + void cancel() + { + service_ = 0; + } + + private: + service_type* service_; + impl_type& impl_; + }; +}; + +} // namespace asio +} // namespace boost + +#include + +#endif // BOOST_ASIO_BASIC_DATAGRAM_SOCKET_HPP diff --git a/include/boost/asio/basic_deadline_timer.hpp b/include/boost/asio/basic_deadline_timer.hpp new file mode 100644 index 00000000..134bce19 --- /dev/null +++ b/include/boost/asio/basic_deadline_timer.hpp @@ -0,0 +1,385 @@ +// +// basic_deadline_timer.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2005 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_BASIC_DEADLINE_TIMER_HPP +#define BOOST_ASIO_BASIC_DEADLINE_TIMER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include + +#include +#include +#include +#include + +#include +#include +#include + +namespace boost { +namespace asio { + +/// Provides waitable timer functionality. +/** + * The basic_deadline_timer class template provides the ability to perform a + * blocking or asynchronous wait for a timer to expire. + * + * Most applications will use the boost::asio::deadline_timer typedef. + * + * @par Thread Safety: + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Unsafe. + * + * @par Concepts: + * Async_Object, Error_Source. + * + * @sa @ref deadline_timer_reset + * + * @par Examples: + * Performing a blocking wait: + * @code + * // Construct a timer without setting an expiry time. + * boost::asio::deadline_timer timer(demuxer); + * + * // Set an expiry time relative to now. + * timer.expires_from_now(boost::posix_time::seconds(5)); + * + * // Wait for the timer to expire. + * timer.wait(); + * @endcode + * + * @par + * Performing an asynchronous wait: + * @code + * void handler(const boost::asio::error& error) + * { + * if (!error) + * { + * // Timer expired. + * } + * } + * + * ... + * + * // Construct a timer with an absolute expiry time. + * boost::asio::deadline_timer timer(demuxer, + * boost::posix_time::time_from_string("2005-12-07 23:59:59.000")); + * + * // Start an asynchronous wait. + * timer.async_wait(handler); + * @endcode + */ +template +class basic_deadline_timer + : private noncopyable +{ +public: + /// The type of the service that will be used to provide timer operations. + typedef Service service_type; + + /// The native implementation type of the timer. + typedef typename service_type::impl_type impl_type; + + /// The demuxer type for this asynchronous type. + typedef typename service_type::demuxer_type demuxer_type; + + /// The type used for reporting errors. + typedef boost::asio::error error_type; + + /// The time type. + typedef typename service_type::time_type time_type; + + /// The duration type. + typedef typename service_type::duration_type duration_type; + + /// Constructor. + /** + * This constructor creates a timer without setting an expiry time. The + * expires_at() or expires_from_now() functions must be called to set an + * expiry time before the timer can be waited on. + * + * @param d The demuxer object that the timer will use to dispatch handlers + * for any asynchronous operations performed on the timer. + */ + explicit basic_deadline_timer(demuxer_type& d) + : service_(d.get_service(service_factory())), + impl_(service_.null()) + { + service_.create(impl_); + } + + /// Constructor to set a particular expiry time as an absolute time. + /** + * This constructor creates a timer and sets the expiry time. + * + * @param d The demuxer object that the timer will use to dispatch handlers + * for any asynchronous operations performed on the timer. + * + * @param expiry_time The expiry time to be used for the timer, expressed + * as an absolute time. + */ + basic_deadline_timer(demuxer_type& d, const time_type& expiry_time) + : service_(d.get_service(service_factory())), + impl_(service_.null()) + { + service_.create(impl_); + destroy_on_block_exit auto_destroy(service_, impl_); + service_.expires_at(impl_, expiry_time); + auto_destroy.cancel(); + } + + /// Constructor to set a particular expiry time relative to now. + /** + * This constructor creates a timer and sets the expiry time. + * + * @param d The demuxer object that the timer will use to dispatch handlers + * for any asynchronous operations performed on the timer. + * + * @param expiry_time The expiry time to be used for the timer, relative to + * now. + */ + basic_deadline_timer(demuxer_type& d, const duration_type& expiry_time) + : service_(d.get_service(service_factory())), + impl_(service_.null()) + { + service_.create(impl_); + destroy_on_block_exit auto_destroy(service_, impl_); + service_.expires_from_now(impl_, expiry_time); + auto_destroy.cancel(); + } + + /// Destructor. + ~basic_deadline_timer() + { + service_.destroy(impl_); + } + + /// Get the demuxer associated with the asynchronous object. + /** + * This function may be used to obtain the demuxer object that the timer uses + * to dispatch handlers for asynchronous operations. + * + * @return A reference to the demuxer object that the timer will use to + * dispatch handlers. Ownership is not transferred to the caller. + */ + demuxer_type& demuxer() + { + return service_.demuxer(); + } + + /// Get the underlying implementation in the native type. + /** + * This function may be used to obtain the underlying implementation of the + * timer. This is intended to allow access to native timer functionality that + * is not otherwise provided. + */ + impl_type impl() + { + return impl_; + } + + /// Get the timer's expiry time as an absolute time. + /** + * This function may be used to obtain the timer's current expiry time. + * Whether the timer has expired or not does not affect this value. + */ + time_type expires_at() const + { + return service_.expires_at(impl_); + } + + /// Set the timer's expiry time as an absolute time. + /** + * This function sets the expiry time. + * + * @param expiry_time The expiry time to be used for the timer. + * + * @note Modifying the expiry time of a timer while it is active (where + * active means there are asynchronous waits on the timer) has undefined + * behaviour. See @ref deadline_timer_reset for information on how to safely + * alter a timer's expiry in this case. + */ + void expires_at(const time_type& expiry_time) + { + service_.expires_at(impl_, expiry_time); + } + + /// Get the timer's expiry time relative to now. + /** + * This function may be used to obtain the timer's current expiry time. + * Whether the timer has expired or not does not affect this value. + */ + duration_type expires_from_now() const + { + return service_.expires_from_now(impl_); + } + + /// Set the timer's expiry time relative to now. + /** + * This function sets the expiry time. + * + * @param expiry_time The expiry time to be used for the timer. + * + * @note Modifying the expiry time of a timer while it is active (where + * active means there are asynchronous waits on the timer) has undefined + * behaviour. See @ref deadline_timer_reset for information on how to safely + * alter a timer's expiry in this case. + */ + void expires_from_now(const duration_type& expiry_time) + { + service_.expires_from_now(impl_, expiry_time); + } + + /// Cancel any asynchronous operations that are waiting on the timer. + /** + * This function forces the completion of any pending asynchronous wait + * operations against the timer. The handler for each cancelled operation + * will be invoked with the boost::asio::error::operation_aborted error code. + * + * Cancelling the timer does not change the expiry time. + * + * @return The number of asynchronous operations that were cancelled. + */ + std::size_t cancel() + { + return service_.cancel(impl_); + } + + /// Perform a blocking wait on the timer. + /** + * This function is used to wait for the timer to expire. This function + * blocks and does not return until the timer has expired. + * + * @throws boost::asio::error Thrown on failure. + */ + void wait() + { + service_.wait(impl_); + } + + /// Start an asynchronous wait on the timer. + /** + * This function may be used to initiate an asynchronous wait against the + * timer. It always returns immediately. + * + * For each call to async_wait(), the supplied handler will be called exactly + * once. The handler will be called when: + * + * @li The timer has expired. + * + * @li The timer was cancelled, in which case the handler is passed the error + * code boost::asio::error::operation_aborted. + * + * @param handler The handler to be called when the timer expires. Copies + * will be made of the handler as required. The function signature of the + * handler must be: + * @code void handler( + * const boost::asio::error& error // Result of operation + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * boost::asio::demuxer::post(). + */ + template + void async_wait(Handler handler) + { + service_.async_wait(impl_, handler); + } + +private: + /// The backend service implementation. + service_type& service_; + + /// The underlying native implementation. + impl_type impl_; + + // Helper class to automatically destroy the implementation on block exit. + class destroy_on_block_exit + { + public: + destroy_on_block_exit(service_type& service, impl_type& impl) + : service_(&service), impl_(impl) + { + } + + ~destroy_on_block_exit() + { + if (service_) + { + service_->destroy(impl_); + } + } + + void cancel() + { + service_ = 0; + } + + private: + service_type* service_; + impl_type& impl_; + }; +}; + +/** + * @page deadline_timer_reset Changing an active deadline_timer's expiry time + * + * Changing the expiry time of a timer while there are asynchronous waits on it + * has undefined behaviour. To safely change a timer's expiry, pending + * asynchronous waits need to be cancelled first. This works as follows: + * + * @li The boost::asio::basic_deadline_timer::cancel() function returns the + * number of asynchronous waits that were cancelled. If it returns 0 then you + * were too late and the wait handler has already been executed, or will soon be + * executed. If it returns 1 then the wait handler was successfully cancelled. + * + * @li If a wait handler is cancelled, the boost::asio::error passed to it + * contains the value boost::asio::error::operation_aborted. + * + * For example, to reset a timer's expiry time in response to some event you + * would do something like this: + * + * @code + * void on_some_event() + * { + * if (my_timer.cancel() > 0) + * { + * // We managed to cancel the timer. Set new expiry time. + * my_timer.expires_from_now(seconds(5)); + * my_timer.async_wait(on_timeout); + * } + * else + * { + * // Too late, timer has already expired! + * } + * } + * + * void on_timeout(const boost::asio::error& e) + * { + * if (e != boost::asio::error::operation_aborted) + * { + * // Timer was not cancelled, take necessary action. + * } + * } + * @endcode + * + * @sa boost::asio::basic_deadline_timer + */ + +} // namespace asio +} // namespace boost + +#include + +#endif // BOOST_ASIO_BASIC_DEADLINE_TIMER_HPP diff --git a/include/boost/asio/basic_demuxer.hpp b/include/boost/asio/basic_demuxer.hpp new file mode 100644 index 00000000..39f66191 --- /dev/null +++ b/include/boost/asio/basic_demuxer.hpp @@ -0,0 +1,341 @@ +// +// basic_demuxer.hpp +// ~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2005 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_BASIC_DEMUXER_HPP +#define BOOST_ASIO_BASIC_DEMUXER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace boost { +namespace asio { + +/// Provides core event demultiplexing functionality. +/** + * The basic_demuxer class template provides the core event demultiplexing + * functionality for users of the asynchronous I/O objects, including: + * + * @li boost::asio::stream_socket + * @li boost::asio::datagram_socket + * @li boost::asio::socket_acceptor + * @li boost::asio::deadline_timer. + * + * The basic_demuxer class template also includes facilities intended for + * developers of custom asynchronous services. + * + * Most applications will use the boost::asio::demuxer typedef. + * + * @par Thread Safety: + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Safe, with the exception that calling reset() + * while there are unfinished run() calls results in undefined behaviour. + * + * @par Concepts: + * Dispatcher. + * + * @sa \ref demuxer_handler_exception + */ +template +class basic_demuxer + : private noncopyable +{ +public: + /// The type of the service that will be used to provide demuxer operations. + typedef Demuxer_Service service_type; + + /// The allocator type for the demuxer. + typedef typename service_type::allocator_type allocator_type; + + /// Default constructor. + basic_demuxer() + : service_registry_(*this), + service_(get_service(service_factory())) + { + } + + /// Construct using the supplied service_factory to get the demuxer service. + explicit basic_demuxer(const service_factory& factory) + : service_registry_(*this), + service_(get_service(factory)) + { + } + + /// Return a copy of the allocator associated with the demuxer. + /** + * The get_allocator() returns a copy of the allocator object used by the + * demuxer. + * + * @return A copy of the demuxer's allocator. + */ + allocator_type get_allocator() const + { + return service_.get_allocator(); + } + + /// Run the demuxer's event processing loop. + /** + * The run() function blocks until all work has finished and there are no + * more handlers to be dispatched, or until the demuxer has been interrupted. + * + * Multiple threads may call the run() function to set up a pool of threads + * from which the demuxer may execute handlers. + * + * The run() function may be safely called again once it has completed only + * after a call to reset(). + */ + void run() + { + service_.run(); + } + + /// Interrupt the demuxer's event processing loop. + /** + * This function does not block, but instead simply signals to the demuxer + * that all invocations of its run() member function should return as soon as + * possible. + * + * Note that if the run() function is interrupted and is not called again + * later then its work may not have finished and handlers may not be + * delivered. In this case a demuxer implementation is not required to make + * any guarantee that the resources associated with unfinished work will be + * cleaned up. + */ + void interrupt() + { + service_.interrupt(); + } + + /// Reset the demuxer in preparation for a subsequent run() invocation. + /** + * This function must be called prior to any second or later set of + * invocations of the run() function. It allows the demuxer to reset any + * internal state, such as an interrupt flag. + * + * This function must not be called while there are any unfinished calls to + * the run() function. + */ + void reset() + { + service_.reset(); + } + + /// Request the demuxer to invoke the given handler. + /** + * This function is used to ask the demuxer to execute the given handler. + * + * The demuxer guarantees that the handler will only be called in a thread in + * which the run() member function is currently being invoked. The handler + * may be executed inside this function if the guarantee can be met. + * + * @param handler The handler to be called. The demuxer will make + * a copy of the handler object as required. The function signature of the + * handler must be: @code void handler(); @endcode + */ + template + void dispatch(Handler handler) + { + service_.dispatch(handler); + } + + /// Request the demuxer to invoke the given handler and return immediately. + /** + * This function is used to ask the demuxer to execute the given handler, but + * without allowing the demuxer to call the handler from inside this + * function. + * + * The demuxer guarantees that the handler will only be called in a thread in + * which the run() member function is currently being invoked. + * + * @param handler The handler to be called. The demuxer will make + * a copy of the handler object as required. The function signature of the + * handler must be: @code void handler(); @endcode + */ + template + void post(Handler handler) + { + service_.post(handler); + } + + /// Create a new handler that automatically dispatches the wrapped handler + /// on the demuxer. + /** + * This function is used to create a new handler function object that, when + * invoked, will automatically pass the wrapped handler to the demuxer's + * dispatch function. + * + * @param handler The handler to be wrapped. The demuxer will make a copy of + * the handler object as required. The function signature of the handler must + * be: @code void handler(A1 a1, ... An an); @endcode + * + * @return A function object that, when invoked, passes the wrapped handler to + * the demuxer's dispatch function. Given a function object with the + * signature: + * @code R f(A1 a1, ... An an); @endcode + * If this function object is passed to the wrap function like so: + * @code demuxer.wrap(f); @endcode + * then the return value is a function object with the signature + * @code void g(A1 a1, ... An an); @endcode + * that, when invoked, executes code equivalent to: + * @code demuxer.dispatch(boost::bind(f, a1, ... an)); @endcode + */ + template +#if defined(GENERATING_DOCUMENTATION) + unspecified +#else + detail::wrapped_handler, Handler> +#endif + wrap(Handler handler) + { + return detail::wrapped_handler, Handler>( + *this, handler); + } + + /// Obtain the service interface corresponding to the given type. + /** + * This function is used to locate a service interface that corresponds to + * the given service type. If there is no existing implementation of the + * service, then the demuxer will use the supplied factory to create a new + * instance. + * + * @param factory The factory to use to create the service. + * + * @return The service interface implementing the specified service type. + * Ownership of the service interface is not transferred to the caller. + */ + template + Service& get_service(service_factory factory) + { + return service_registry_.get_service(factory); + } + + class work; + friend class work; + +private: +#if defined(BOOST_WINDOWS) + detail::winsock_init<> init_; +#else + detail::signal_init<> init_; +#endif + + /// The service registry. + detail::service_registry > service_registry_; + + /// The underlying demuxer service implementation. + Demuxer_Service& service_; +}; + +/// Class to inform the demuxer when it has work to do. +/** + * The work class is used to inform the demuxer when work starts and finishes. + * This ensures that the demuxer's run() function will not exit while work is + * underway, and that it does exit when there is no unfinished work remaining. + * + * The work class is copy-constructible so that it may be used as a data member + * in a handler class. It is not assignable. + */ +template +class basic_demuxer::work +{ +public: + /// Constructor notifies the demuxer that work is starting. + /** + * The constructor is used to inform the demuxer that some work has begun. + * This ensures that the demuxer's run() function will not exit while the work + * is underway. + */ + explicit work(basic_demuxer& demuxer) + : service_(demuxer.service_) + { + service_.work_started(); + } + + /// Copy constructor notifies the demuxer that work is starting. + /** + * The constructor is used to inform the demuxer that some work has begun. + * This ensures that the demuxer's run() function will not exit while the work + * is underway. + */ + work(const work& other) + : service_(other.service_) + { + service_.work_started(); + } + + /// Destructor notifies the demuxer that the work is complete. + /** + * The destructor is used to inform the demuxer that some work has finished. + * Once the count of unfinished work reaches zero, the demuxer's run() + * function is permitted to exit. + */ + ~work() + { + service_.work_finished(); + } + +private: + // Prevent assignment. + void operator=(const work& other); + + /// The underlying demuxer service implementation. + Demuxer_Service& service_; +}; + +/** + * @page demuxer_handler_exception Effect of exceptions thrown from handlers + * + * If an exception is thrown from a handler, the exception is allowed to + * propagate through the throwing thread's invocation of + * boost::asio::demuxer::run(). No other threads that are calling + * boost::asio::demuxer::run() are affected. It is then the responsibility of + * the application to catch the exception. + * + * After the exception has been caught, the boost::asio::demuxer::run() call + * may be restarted @em without the need for an intervening call to + * boost::asio::demuxer::reset(). This allows the thread to rejoin the + * demuxer's thread pool without impacting any other threads in the + * pool. + * + * @par Example: + * @code + * boost::asio::demuxer demuxer; + * ... + * for (;;) + * { + * try + * { + * demuxer.run(); + * break; // run() exited normally + * } + * catch (my_exception& e) + * { + * // Deal with exception as appropriate. + * } + * } + * @endcode + */ + +} // namespace asio +} // namespace boost + +#include + +#endif // BOOST_ASIO_BASIC_DEMUXER_HPP diff --git a/include/boost/asio/basic_locking_dispatcher.hpp b/include/boost/asio/basic_locking_dispatcher.hpp new file mode 100644 index 00000000..ad45fc0a --- /dev/null +++ b/include/boost/asio/basic_locking_dispatcher.hpp @@ -0,0 +1,183 @@ +// +// basic_locking_dispatcher.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2005 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_BASIC_LOCKING_DISPATCHER_HPP +#define BOOST_ASIO_BASIC_LOCKING_DISPATCHER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include + +#include +#include +#include +#include + +namespace boost { +namespace asio { + +/// Provides serialised handler execution. +/** + * The basic_locking_dispatcher class template provides the ability to post + * and dispatch handlers with the guarantee that none of those handlers will + * execute concurrently. + * + * Most applications will use the boost::asio::locking_dispatcher typedef. + * + * @par Thread Safety: + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Safe. + * + * @par Concepts: + * Async_Object, Dispatcher, Error_Source. + */ +template +class basic_locking_dispatcher + : private noncopyable +{ +public: + /// The type of the service that will be used to provide locking dispatcher + /// operations. + typedef Service service_type; + + /// The native implementation type of the locking dispatcher. + typedef typename service_type::impl_type impl_type; + + /// The demuxer type for this dispatcher. + typedef typename service_type::demuxer_type demuxer_type; + + /// The type used for reporting errors. + typedef boost::asio::error error_type; + + /// Constructor. + /** + * Constructs the locking dispatcher. + * + * @param d The demuxer object that the locking dispatcher will use to + * dispatch handlers that are ready to be run. + */ + explicit basic_locking_dispatcher(demuxer_type& d) + : service_(d.get_service(service_factory())), + impl_(service_.null()) + { + service_.create(impl_); + } + + /// Destructor. + ~basic_locking_dispatcher() + { + service_.destroy(impl_); + } + + /// Get the demuxer associated with the asynchronous object. + /** + * This function may be used to obtain the demuxer object that the locking + * dispatcher uses to dispatch handlers for asynchronous operations. + * + * @return A reference to the demuxer object that the dispatcher will use to + * dispatch handlers. Ownership is not transferred to the caller. + */ + demuxer_type& demuxer() + { + return service_.demuxer(); + } + + /// Request the dispatcher to invoke the given handler. + /** + * This function is used to ask the dispatcher to execute the given handler. + * + * The dispatcher guarantees that the handler will only be called in a thread + * in which the underlying demuxer's run member function is currently being + * invoked. It also guarantees that only one handler executed through this + * dispatcher will be invoked at a time. The handler may be executed inside + * this function if the guarantee can be met. + * + * @param handler The handler to be called. The dispatcher will make + * a copy of the handler object as required. The function signature of the + * handler must be: @code void handler(); @endcode + */ + template + void dispatch(Handler handler) + { + service_.dispatch(impl_, handler); + } + + /// Request the dispatcher to invoke the given handler and return + /// immediately. + /** + * This function is used to ask the dispatcher to execute the given handler, + * but without allowing the dispatcher to call the handler from inside this + * function. + * + * The dispatcher guarantees that the handler will only be called in a thread + * in which the underlying demuxer's run member function is currently being + * invoked. It also guarantees that only one handler executed through this + * dispatcher will be invoked at a time. + * + * @param handler The handler to be called. The dispatcher will make + * a copy of the handler object as required. The function signature of the + * handler must be: @code void handler(); @endcode + */ + template + void post(Handler handler) + { + service_.post(impl_, handler); + } + + /// Create a new handler that automatically dispatches the wrapped handler + /// on the dispatcher. + /** + * This function is used to create a new handler function object that, when + * invoked, will automatically pass the wrapped handler to the dispatcher's + * dispatch function. + * + * @param handler The handler to be wrapped. The dispatcher will make a copy + * of the handler object as required. The function signature of the handler + * must be: @code void handler(A1 a1, ... An an); @endcode + * + * @return A function object that, when invoked, passes the wrapped handler to + * the dispatcher's dispatch function. Given a function object with the + * signature: + * @code R f(A1 a1, ... An an); @endcode + * If this function object is passed to the wrap function like so: + * @code dispatcher.wrap(f); @endcode + * then the return value is a function object with the signature + * @code void g(A1 a1, ... An an); @endcode + * that, when invoked, executes code equivalent to: + * @code dispatcher.dispatch(boost::bind(f, a1, ... an)); @endcode + */ + template +#if defined(GENERATING_DOCUMENTATION) + unspecified +#else + detail::wrapped_handler, Handler> +#endif + wrap(Handler handler) + { + return detail::wrapped_handler, Handler>( + *this, handler); + } + +private: + /// The backend service implementation. + service_type& service_; + + /// The underlying native implementation. + impl_type impl_; +}; + +} // namespace asio +} // namespace boost + +#include + +#endif // BOOST_ASIO_BASIC_LOCKING_DISPATCHER_HPP diff --git a/include/boost/asio/basic_socket_acceptor.hpp b/include/boost/asio/basic_socket_acceptor.hpp new file mode 100644 index 00000000..166ea409 --- /dev/null +++ b/include/boost/asio/basic_socket_acceptor.hpp @@ -0,0 +1,798 @@ +// +// basic_socket_acceptor.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2005 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_BASIC_SOCKET_ACCEPTOR_HPP +#define BOOST_ASIO_BASIC_SOCKET_ACCEPTOR_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include + +#include +#include +#include +#include +#include + +namespace boost { +namespace asio { + +/// Provides the ability to accept new connections. +/** + * The basic_socket_acceptor class template is used for accepting new socket + * connections. + * + * Most applications would use the boost::asio::socket_acceptor typedef. + * + * @par Thread Safety: + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Unsafe. + * + * @par Concepts: + * Async_Object, Error_Source. + * + * @par Example: + * Opening a socket acceptor with the SO_REUSEADDR option enabled: + * @code + * boost::asio::socket_acceptor acceptor(demuxer); + * boost::asio::ipv4::tcp::endpoint endpoint(port); + * acceptor.open(endpoint.protocol()); + * acceptor.set_option(boost::asio::socket_acceptor::reuse_address(true)); + * acceptor.bind(endpoint); + * acceptor.listen(); + * @endcode + */ +template +class basic_socket_acceptor + : public socket_base, + private noncopyable +{ +public: + /// The type of the service that will be used to provide accept operations. + typedef Service service_type; + + /// The native implementation type of the socket acceptor. + typedef typename service_type::impl_type impl_type; + + /// The demuxer type for this asynchronous type. + typedef typename service_type::demuxer_type demuxer_type; + + /// The type used for reporting errors. + typedef boost::asio::error error_type; + + /// Construct an acceptor without opening it. + /** + * This constructor creates an acceptor without opening it to listen for new + * connections. The open() function must be called before the acceptor can + * accept new socket connections. + * + * @param d The demuxer object that the acceptor will use to dispatch + * handlers for any asynchronous operations performed on the acceptor. + */ + explicit basic_socket_acceptor(demuxer_type& d) + : service_(d.get_service(service_factory())), + impl_(service_.null()) + { + } + + /// Construct an acceptor opened on the given endpoint. + /** + * This constructor creates an acceptor and automatically opens it to listen + * for new connections on the specified endpoint. + * + * @param d The demuxer object that the acceptor will use to dispatch + * handlers for any asynchronous operations performed on the acceptor. + * + * @param endpoint An endpoint on the local machine on which the acceptor + * will listen for new connections. + * + * @param listen_backlog The maximum length of the queue of pending + * connections. A value of 0 means use the default queue length. + * + * @throws boost::asio::error Thrown on failure. + * + * @note This constructor is equivalent to the following code: + * @code + * boost::asio::socket_acceptor acceptor(demuxer); + * acceptor.open(endpoint.protocol()); + * acceptor.bind(endpoint); + * acceptor.listen(listen_backlog); + * @endcode + */ + template + basic_socket_acceptor(demuxer_type& d, const Endpoint& endpoint, + int listen_backlog = 0) + : service_(d.get_service(service_factory())), + impl_(service_.null()) + { + service_.open(impl_, endpoint.protocol(), throw_error()); + close_on_block_exit auto_close(service_, impl_); + service_.bind(impl_, endpoint, throw_error()); + service_.listen(impl_, listen_backlog, throw_error()); + auto_close.cancel(); + } + + /// Destructor. + ~basic_socket_acceptor() + { + service_.close(impl_, ignore_error()); + } + + /// Get the demuxer associated with the asynchronous object. + /** + * This function may be used to obtain the demuxer object that the acceptor + * uses to dispatch handlers for asynchronous operations. + * + * @return A reference to the demuxer object that acceptor will use to + * dispatch handlers. Ownership is not transferred to the caller. + */ + demuxer_type& demuxer() + { + return service_.demuxer(); + } + + /// Open the acceptor using the specified protocol. + /** + * This function opens the socket acceptor so that it will use the specified + * protocol. + * + * @param protocol An object specifying which protocol is to be used. + * + * @par Example: + * @code + * boost::asio::socket_acceptor acceptor(demuxer); + * acceptor.open(boost::asio::ipv4::tcp()); + * @endcode + */ + template + void open(const Protocol& protocol) + { + service_.open(impl_, protocol, throw_error()); + } + + /// Open the acceptor using the specified protocol. + /** + * This function opens the socket acceptor so that it will use the specified + * protocol. + * + * @param protocol An object specifying which protocol is to be used. + * + * @param error_handler The handler to be called when an error occurs. Copies + * will be made of the handler as required. The function signature of the + * handler must be: + * @code void error_handler( + * const boost::asio::error& error // Result of operation + * ); @endcode + * + * @par Example: + * @code + * boost::asio::socket_acceptor acceptor(demuxer); + * boost::asio::error error; + * acceptor.open(boost::asio::ipv4::tcp(), boost::asio::assign_error(error)); + * if (error) + * { + * // An error occurred. + * } + * @endcode + */ + template + void open(const Protocol& protocol, Error_Handler error_handler) + { + service_.open(impl_, protocol, error_handler); + } + + /// Bind the acceptor to the given local endpoint. + /** + * This function binds the socket acceptor to the specified endpoint on the + * local machine. + * + * @param endpoint An endpoint on the local machine to which the socket + * acceptor will be bound. + * + * @throws boost::asio::error Thrown on failure. + * + * @par Example: + * @code + * boost::asio::socket_acceptor acceptor(demuxer); + * acceptor.open(boost::asio::ipv4::tcp()); + * acceptor.bind(boost::asio::ipv4::tcp::endpoint(12345)); + * @endcode + */ + template + void bind(const Endpoint& endpoint) + { + service_.bind(impl_, endpoint, throw_error()); + } + + /// Bind the acceptor to the given local endpoint. + /** + * This function binds the socket acceptor to the specified endpoint on the + * local machine. + * + * @param endpoint An endpoint on the local machine to which the socket + * acceptor will be bound. + * + * @param error_handler The handler to be called when an error occurs. Copies + * will be made of the handler as required. The function signature of the + * handler must be: + * @code void error_handler( + * const boost::asio::error& error // Result of operation + * ); @endcode + * + * @par Example: + * @code + * boost::asio::socket_acceptor acceptor(demuxer); + * acceptor.open(boost::asio::ipv4::tcp()); + * boost::asio::error error; + * acceptor.bind(boost::asio::ipv4::tcp::endpoint(12345), + * boost::asio::assign_error(error)); + * if (error) + * { + * // An error occurred. + * } + * @endcode + */ + template + void bind(const Endpoint& endpoint, Error_Handler error_handler) + { + service_.bind(impl_, endpoint, error_handler); + } + + /// Place the acceptor into the state where it will listen for new + /// connections. + /** + * This function puts the socket acceptor into the state where it may accept + * new connections. + * + * @param backlog The maximum length of the queue of pending connections. A + * value of 0 means use the default queue length. + */ + void listen(int backlog = 0) + { + service_.listen(impl_, backlog, throw_error()); + } + + /// Place the acceptor into the state where it will listen for new + /// connections. + /** + * This function puts the socket acceptor into the state where it may accept + * new connections. + * + * @param backlog The maximum length of the queue of pending connections. A + * value of 0 means use the default queue length. + * + * @param error_handler The handler to be called when an error occurs. Copies + * will be made of the handler as required. The function signature of the + * handler must be: + * @code void error_handler( + * const boost::asio::error& error // Result of operation + * ); @endcode + * + * @par Example: + * @code + * boost::asio::socket_acceptor acceptor(demuxer); + * ... + * boost::asio::error error; + * acceptor.listen(0, boost::asio::assign_error(error)); + * if (error) + * { + * // An error occurred. + * } + * @endcode + */ + template + void listen(int backlog, Error_Handler error_handler) + { + service_.listen(impl_, backlog, error_handler); + } + + /// Close the acceptor. + /** + * This function is used to close the acceptor. Any asynchronous accept + * operations will be cancelled immediately. + * + * A subsequent call to open() is required before the acceptor can again be + * used to again perform socket accept operations. + * + * @throws boost::asio::error Thrown on failure. + */ + void close() + { + service_.close(impl_, throw_error()); + } + + /// Close the acceptor. + /** + * This function is used to close the acceptor. Any asynchronous accept + * operations will be cancelled immediately. + * + * A subsequent call to open() is required before the acceptor can again be + * used to again perform socket accept operations. + * + * @param error_handler The handler to be called when an error occurs. Copies + * will be made of the handler as required. The function signature of the + * handler must be: + * @code void error_handler( + * const boost::asio::error& error // Result of operation + * ); @endcode + * + * @par Example: + * @code + * boost::asio::socket_acceptor acceptor(demuxer); + * ... + * boost::asio::error error; + * acceptor.close(boost::asio::assign_error(error)); + * if (error) + * { + * // An error occurred. + * } + * @endcode + */ + template + void close(Error_Handler error_handler) + { + service_.close(impl_, error_handler); + } + + /// Get the underlying implementation in the native type. + /** + * This function may be used to obtain the underlying implementation of the + * socket acceptor. This is intended to allow access to native socket + * functionality that is not otherwise provided. + */ + impl_type impl() + { + return impl_; + } + + /// Set an option on the acceptor. + /** + * This function is used to set an option on the acceptor. + * + * @param option The new option value to be set on the acceptor. + * + * @throws boost::asio::error Thrown on failure. + * + * @sa Socket_Option @n + * boost::asio::socket_base::reuse_address + * + * @par Example: + * Setting the SOL_SOCKET/SO_REUSEADDR option: + * @code + * boost::asio::socket_acceptor acceptor(demuxer); + * ... + * boost::asio::socket_acceptor::reuse_address option(true); + * acceptor.set_option(option); + * @endcode + */ + template + void set_option(const Option& option) + { + service_.set_option(impl_, option, throw_error()); + } + + /// Set an option on the acceptor. + /** + * This function is used to set an option on the acceptor. + * + * @param option The new option value to be set on the acceptor. + * + * @param error_handler The handler to be called when an error occurs. Copies + * will be made of the handler as required. The function signature of the + * handler must be: + * @code void error_handler( + * const boost::asio::error& error // Result of operation + * ); @endcode + * + * @sa Socket_Option @n + * boost::asio::socket_base::reuse_address + * + * @par Example: + * Setting the SOL_SOCKET/SO_REUSEADDR option: + * @code + * boost::asio::socket_acceptor acceptor(demuxer); + * ... + * boost::asio::socket_acceptor::reuse_address option(true); + * boost::asio::error error; + * acceptor.set_option(option, boost::asio::assign_error(error)); + * if (error) + * { + * // An error occurred. + * } + * @endcode + */ + template + void set_option(const Option& option, Error_Handler error_handler) + { + service_.set_option(impl_, option, error_handler); + } + + /// Get an option from the acceptor. + /** + * This function is used to get the current value of an option on the + * acceptor. + * + * @param option The option value to be obtained from the acceptor. + * + * @throws boost::asio::error Thrown on failure. + * + * @sa Socket_Option @n + * boost::asio::socket_base::reuse_address + * + * @par Example: + * Getting the value of the SOL_SOCKET/SO_REUSEADDR option: + * @code + * boost::asio::socket_acceptor acceptor(demuxer); + * ... + * boost::asio::socket_acceptor::reuse_address option; + * acceptor.get_option(option); + * bool is_set = option.get(); + * @endcode + */ + template + void get_option(Option& option) + { + service_.get_option(impl_, option, throw_error()); + } + + /// Get an option from the acceptor. + /** + * This function is used to get the current value of an option on the + * acceptor. + * + * @param option The option value to be obtained from the acceptor. + * + * @param error_handler The handler to be called when an error occurs. Copies + * will be made of the handler as required. The function signature of the + * handler must be: + * @code void error_handler( + * const boost::asio::error& error // Result of operation + * ); @endcode + * + * @sa Socket_Option @n + * boost::asio::socket_base::reuse_address + * + * @par Example: + * Getting the value of the SOL_SOCKET/SO_REUSEADDR option: + * @code + * boost::asio::socket_acceptor acceptor(demuxer); + * ... + * boost::asio::socket_acceptor::reuse_address option; + * boost::asio::error error; + * acceptor.get_option(option, boost::asio::assign_error(error)); + * if (error) + * { + * // An error occurred. + * } + * bool is_set = option.get(); + * @endcode + */ + template + void get_option(Option& option, Error_Handler error_handler) + { + service_.get_option(impl_, option, error_handler); + } + + /// Get the local endpoint of the acceptor. + /** + * This function is used to obtain the locally bound endpoint of the + * acceptor. + * + * @param endpoint An endpoint object that receives the local endpoint of the + * acceptor. + * + * @throws boost::asio::error Thrown on failure. + * + * @par Example: + * @code + * boost::asio::socket_acceptor acceptor(demuxer); + * ... + * boost::asio::ipv4::tcp::endpoint endpoint; + * acceptor.get_local_endpoint(endpoint); + * @endcode + */ + template + void get_local_endpoint(Endpoint& endpoint) + { + service_.get_local_endpoint(impl_, endpoint, throw_error()); + } + + /// Get the local endpoint of the acceptor. + /** + * This function is used to obtain the locally bound endpoint of the + * acceptor. + * + * @param endpoint An endpoint object that receives the local endpoint of the + * acceptor. + * + * @param error_handler The handler to be called when an error occurs. Copies + * will be made of the handler as required. The function signature of the + * handler must be: + * @code void error_handler( + * const boost::asio::error& error // Result of operation + * ); @endcode + * + * @par Example: + * @code + * boost::asio::socket_acceptor acceptor(demuxer); + * ... + * boost::asio::ipv4::tcp::endpoint endpoint; + * boost::asio::error error; + * acceptor.get_local_endpoint(endpoint, boost::asio::assign_error(error)); + * if (error) + * { + * // An error occurred. + * } + * @endcode + */ + template + void get_local_endpoint(Endpoint& endpoint, Error_Handler error_handler) + { + service_.get_local_endpoint(impl_, endpoint, error_handler); + } + + /// Accept a new connection. + /** + * This function is used to accept a new connection from a peer into the + * given socket. The function call will block until a new connection has been + * accepted successfully or an error occurs. + * + * @param peer The socket into which the new connection will be accepted. + * + * @throws boost::asio::error Thrown on failure. + * + * @par Example: + * @code + * boost::asio::socket_acceptor acceptor(demuxer); + * ... + * boost::asio::stream_socket socket; + * acceptor.accept(socket); + * @endcode + */ + template + void accept(Socket& peer) + { + service_.accept(impl_, to_socket(peer), throw_error()); + } + + /// Accept a new connection. + /** + * This function is used to accept a new connection from a peer into the + * given socket. The function call will block until a new connection has been + * accepted successfully or an error occurs. + * + * @param peer The socket into which the new connection will be accepted. + * + * @param error_handler The handler to be called when an error occurs. Copies + * will be made of the handler as required. The function signature of the + * handler must be: + * @code void error_handler( + * const boost::asio::error& error // Result of operation + * ); @endcode + * + * @par Example: + * @code + * boost::asio::socket_acceptor acceptor(demuxer); + * ... + * boost::asio::stream_socket socket; + * boost::asio::error error; + * acceptor.accept(socket, boost::asio::assign_error(error)); + * if (error) + * { + * // An error occurred. + * } + * @endcode + */ + template + void accept(Socket& peer, Error_Handler error_handler) + { + service_.accept(impl_, to_socket(peer), error_handler); + } + + /// Start an asynchronous accept. + /** + * This function is used to asynchronously accept a new connection into a + * socket. The function call always returns immediately. + * + * @param peer The socket into which the new connection will be accepted. + * Ownership of the peer object is retained by the caller, which must + * guarantee that it is valid until the handler is called. + * + * @param handler The handler to be called when the accept operation + * completes. Copies will be made of the handler as required. The function + * signature of the handler must be: + * @code void handler( + * const boost::asio::error& error // Result of operation + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * boost::asio::demuxer::post(). + * + * @par Example: + * @code + * void accept_handler(const boost::asio::error& error) + * { + * if (!error) + * { + * // Accept succeeded. + * } + * } + * + * ... + * + * boost::asio::socket_acceptor acceptor(demuxer); + * ... + * boost::asio::stream_socket socket; + * acceptor.async_accept(socket, accept_handler); + * @endcode + */ + template + void async_accept(Socket& peer, Handler handler) + { + service_.async_accept(impl_, to_socket(peer), handler); + } + + /// Accept a new connection and obtain the endpoint of the peer + /** + * This function is used to accept a new connection from a peer into the + * given socket, and additionally provide the endpoint of the remote peer. + * The function call will block until a new connection has been accepted + * successfully or an error occurs. + * + * @param peer The socket into which the new connection will be accepted. + * + * @param peer_endpoint An endpoint object which will receive the endpoint of + * the remote peer. + * + * @throws boost::asio::error Thrown on failure. + * + * @par Example: + * @code + * boost::asio::socket_acceptor acceptor(demuxer); + * ... + * boost::asio::stream_socket socket; + * boost::asio::ipv4::tcp::endpoint endpoint; + * acceptor.accept_endpoint(socket, endpoint); + * @endcode + */ + template + void accept_endpoint(Socket& peer, Endpoint& peer_endpoint) + { + service_.accept_endpoint(impl_, to_socket(peer), peer_endpoint, + throw_error()); + } + + /// Accept a new connection and obtain the endpoint of the peer + /** + * This function is used to accept a new connection from a peer into the + * given socket, and additionally provide the endpoint of the remote peer. + * The function call will block until a new connection has been accepted + * successfully or an error occurs. + * + * @param peer The socket into which the new connection will be accepted. + * + * @param peer_endpoint An endpoint object which will receive the endpoint of + * the remote peer. + * + * @param error_handler The handler to be called when an error occurs. Copies + * will be made of the handler as required. The function signature of the + * handler must be: + * @code void error_handler( + * const boost::asio::error& error // Result of operation + * ); @endcode + * + * @par Example: + * @code + * boost::asio::socket_acceptor acceptor(demuxer); + * ... + * boost::asio::stream_socket socket; + * boost::asio::ipv4::tcp::endpoint endpoint; + * boost::asio::error error; + * acceptor.accept_endpoint(socket, endpoint, + * boost::asio::assign_error(error)); + * if (error) + * { + * // An error occurred. + * } + * @endcode + */ + template + void accept_endpoint(Socket& peer, Endpoint& peer_endpoint, + Error_Handler error_handler) + { + service_.accept_endpoint(impl_, to_socket(peer), peer_endpoint, + error_handler); + } + + /// Start an asynchronous accept. + /** + * This function is used to asynchronously accept a new connection into a + * socket, and additionally obtain the endpoint of the remote peer. The + * function call always returns immediately. + * + * @param peer The socket into which the new connection will be accepted. + * Ownership of the peer object is retained by the caller, which must + * guarantee that it is valid until the handler is called. + * + * @param peer_endpoint An endpoint object into which the endpoint of the + * remote peer will be written. Ownership of the peer_endpoint object is + * retained by the caller, which must guarantee that it is valid until the + * handler is called. + * + * @param handler The handler to be called when the accept operation + * completes. Copies will be made of the handler as required. The function + * signature of the handler must be: + * @code void handler( + * const boost::asio::error& error // Result of operation + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * boost::asio::demuxer::post(). + */ + template + void async_accept_endpoint(Socket& peer, Endpoint& peer_endpoint, + Handler handler) + { + service_.async_accept_endpoint(impl_, to_socket(peer), peer_endpoint, + handler); + } + +private: + /// The backend service implementation. + service_type& service_; + + /// The underlying native implementation. + impl_type impl_; + + // Helper function to convert a stack of layers into a socket. + template + typename Socket::lowest_layer_type& to_socket(Socket& peer) + { + return peer.lowest_layer(); + } + + // Helper class to automatically close the implementation on block exit. + class close_on_block_exit + { + public: + close_on_block_exit(service_type& service, impl_type& impl) + : service_(&service), impl_(impl) + { + } + + ~close_on_block_exit() + { + if (service_) + { + service_->close(impl_, ignore_error()); + } + } + + void cancel() + { + service_ = 0; + } + + private: + service_type* service_; + impl_type& impl_; + }; +}; + +} // namespace asio +} // namespace boost + +#include + +#endif // BOOST_ASIO_BASIC_SOCKET_ACCEPTOR_HPP diff --git a/include/boost/asio/basic_stream_socket.hpp b/include/boost/asio/basic_stream_socket.hpp new file mode 100644 index 00000000..76eb86a7 --- /dev/null +++ b/include/boost/asio/basic_stream_socket.hpp @@ -0,0 +1,1321 @@ +// +// basic_stream_socket.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2005 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_BASIC_STREAM_SOCKET_HPP +#define BOOST_ASIO_BASIC_STREAM_SOCKET_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +namespace boost { +namespace asio { + +/// Provides stream-oriented socket functionality. +/** + * The basic_stream_socket class template provides asynchronous and blocking + * stream-oriented socket functionality. + * + * Most applications will use the boost::asio::stream_socket typedef. + * + * @par Thread Safety: + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Unsafe. + * + * @par Concepts: + * Async_Object, Async_Read_Stream, Async_Write_Stream, Error_Source, Stream, + * Sync_Read_Stream, Sync_Write_Stream. + */ +template +class basic_stream_socket + : public socket_base, + private noncopyable +{ +public: + /// The type of the service that will be used to provide socket operations. + typedef Service service_type; + + /// The native implementation type of the stream socket. + typedef typename service_type::impl_type impl_type; + + /// The demuxer type for this asynchronous type. + typedef typename service_type::demuxer_type demuxer_type; + + /// The type used for reporting errors. + typedef boost::asio::error error_type; + + /// A basic_stream_socket is always the lowest layer. + typedef basic_stream_socket lowest_layer_type; + + /// Construct a basic_stream_socket without opening it. + /** + * This constructor creates a stream socket without connecting it to a remote + * peer. The socket needs to be connected or accepted before data can be sent + * or received on it. + * + * @param d The demuxer object that the stream socket will use to dispatch + * handlers for any asynchronous operations performed on the socket. + */ + explicit basic_stream_socket(demuxer_type& d) + : service_(d.get_service(service_factory())), + impl_(service_.null()) + { + } + + /// Destructor. + ~basic_stream_socket() + { + service_.close(impl_, ignore_error()); + } + + /// Get the demuxer associated with the asynchronous object. + /** + * This function may be used to obtain the demuxer object that the stream + * socket uses to dispatch handlers for asynchronous operations. + * + * @return A reference to the demuxer object that stream socket will use to + * dispatch handlers. Ownership is not transferred to the caller. + */ + demuxer_type& demuxer() + { + return service_.demuxer(); + } + + /// Open the socket using the specified protocol. + /** + * This function opens the stream socket so that it will use the specified + * protocol. + * + * @param protocol An object specifying which protocol is to be used. + * + * @throws boost::asio::error Thrown on failure. + * + * @par Example: + * @code + * boost::asio::stream_socket socket(demuxer); + * socket.open(boost::asio::ipv4::tcp()); + * @endcode + */ + template + void open(const Protocol& protocol) + { + service_.open(impl_, protocol, throw_error()); + } + + /// Open the socket using the specified protocol. + /** + * This function opens the stream socket so that it will use the specified + * protocol. + * + * @param protocol An object specifying which protocol is to be used. + * + * @param error_handler The handler to be called when an error occurs. Copies + * will be made of the handler as required. The function signature of the + * handler must be: + * @code void error_handler( + * const boost::asio::error& error // Result of operation + * ); @endcode + * + * @par Example: + * @code + * boost::asio::stream_socket socket(demuxer); + * boost::asio::error error; + * socket.open(boost::asio::ipv4::tcp(), boost::asio::assign_error(error)); + * if (error) + * { + * // An error occurred. + * } + * @endcode + */ + template + void open(const Protocol& protocol, Error_Handler error_handler) + { + service_.open(impl_, protocol, error_handler); + } + + /// Close the socket. + /** + * This function is used to close the stream socket. Any asynchronous send + * or receive operations will be cancelled immediately. + * + * @throws boost::asio::error Thrown on failure. + */ + void close() + { + service_.close(impl_, throw_error()); + } + + /// Close the socket. + /** + * This function is used to close the stream socket. Any asynchronous send + * or receive operations will be cancelled immediately. + * + * @param error_handler The handler to be called when an error occurs. Copies + * will be made of the handler as required. The function signature of the + * handler must be: + * @code void error_handler( + * const boost::asio::error& error // Result of operation + * ); @endcode + * + * @par Example: + * @code + * boost::asio::stream_socket socket(demuxer); + * ... + * boost::asio::error error; + * socket.close(boost::asio::assign_error(error)); + * if (error) + * { + * // An error occurred. + * } + * @endcode + */ + template + void close(Error_Handler error_handler) + { + service_.close(impl_, error_handler); + } + + /// Get a reference to the lowest layer. + /** + * This function returns a reference to the lowest layer in a stack of + * stream layers. Since a basic_stream_socket cannot contain any further + * stream layers, it simply returns a reference to itself. + * + * @return A reference to the lowest layer in the stack of stream layers. + * Ownership is not transferred to the caller. + */ + lowest_layer_type& lowest_layer() + { + return *this; + } + + /// Get the underlying implementation in the native type. + /** + * This function may be used to obtain the underlying implementation of the + * stream socket. This is intended to allow access to native socket + * functionality that is not otherwise provided. + */ + impl_type impl() + { + return impl_; + } + + /// Set the underlying implementation in the native type. + /** + * This function is used by the acceptor implementation to set the underlying + * implementation associated with the stream socket. + * + * @param new_impl The new underlying socket implementation. + */ + void set_impl(impl_type new_impl) + { + service_.assign(impl_, new_impl); + } + + /// Bind the socket to the given local endpoint. + /** + * This function binds the stream socket to the specified endpoint on the + * local machine. + * + * @param endpoint An endpoint on the local machine to which the stream socket + * will be bound. + * + * @throws boost::asio::error Thrown on failure. + * + * @par Example: + * @code + * boost::asio::stream_socket socket(demuxer); + * socket.open(boost::asio::ipv4::tcp()); + * socket.bind(boost::asio::ipv4::tcp::endpoint(12345)); + * @endcode + */ + template + void bind(const Endpoint& endpoint) + { + service_.bind(impl_, endpoint, throw_error()); + } + + /// Bind the socket to the given local endpoint. + /** + * This function binds the stream socket to the specified endpoint on the + * local machine. + * + * @param endpoint An endpoint on the local machine to which the stream socket + * will be bound. + * + * @param error_handler The handler to be called when an error occurs. Copies + * will be made of the handler as required. The function signature of the + * handler must be: + * @code void error_handler( + * const boost::asio::error& error // Result of operation + * ); @endcode + * + * @par Example: + * @code + * boost::asio::stream_socket socket(demuxer); + * socket.open(boost::asio::ipv4::tcp()); + * boost::asio::error error; + * socket.bind(boost::asio::ipv4::tcp::endpoint(12345), + * boost::asio::assign_error(error)); + * if (error) + * { + * // An error occurred. + * } + * @endcode + */ + template + void bind(const Endpoint& endpoint, Error_Handler error_handler) + { + service_.bind(impl_, endpoint, error_handler); + } + + /// Connect a stream socket to the specified endpoint. + /** + * This function is used to connect a stream socket to the specified remote + * endpoint. The function call will block until the connection is successfully + * made or an error occurs. + * + * The socket is automatically opened if it is not already open. If the + * connect fails, and the socket was automatically opened, the socket is + * returned to the closed state. + * + * @param peer_endpoint The remote endpoint to which the socket will be + * connected. + * + * @throws boost::asio::error Thrown on failure. + * + * @par Example: + * @code + * boost::asio::stream_socket socket(demuxer); + * boost::asio::ipv4::tcp::endpoint endpoint(12345, "1.2.3.4"); + * socket.connect(endpoint); + * @endcode + */ + template + void connect(const Endpoint& peer_endpoint) + { + service_.connect(impl_, peer_endpoint, throw_error()); + } + + /// Connect a stream socket to the specified endpoint. + /** + * This function is used to connect a stream socket to the specified remote + * endpoint. The function call will block until the connection is successfully + * made or an error occurs. + * + * The socket is automatically opened if it is not already open. If the + * connect fails, and the socket was automatically opened, the socket is + * returned to the closed state. + * + * @param peer_endpoint The remote endpoint to which the socket will be + * connected. + * + * @param error_handler The handler to be called when an error occurs. Copies + * will be made of the handler as required. The function signature of the + * handler must be: + * @code void error_handler( + * const boost::asio::error& error // Result of operation + * ); @endcode + * + * @par Example: + * @code + * boost::asio::stream_socket socket(demuxer); + * boost::asio::ipv4::tcp::endpoint endpoint(12345, "1.2.3.4"); + * boost::asio::error error; + * socket.connect(endpoint, boost::asio::assign_error(error)); + * if (error) + * { + * // An error occurred. + * } + * @endcode + */ + template + void connect(const Endpoint& peer_endpoint, Error_Handler error_handler) + { + service_.connect(impl_, peer_endpoint, error_handler); + } + + /// Start an asynchronous connect. + /** + * This function is used to asynchronously connect a stream socket to the + * specified remote endpoint. The function call always returns immediately. + * + * The socket is automatically opened if it is not already open. If the + * connect fails, and the socket was automatically opened, the socket is + * returned to the closed state. + * + * @param peer_endpoint The remote endpoint to which the socket will be + * connected. Copies will be made of the endpoint object as required. + * + * @param handler The handler to be called when the connection operation + * completes. Copies will be made of the handler as required. The function + * signature of the handler must be: + * @code void handler( + * const boost::asio::error& error // Result of operation + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * boost::asio::demuxer::post(). + * + * @par Example: + * @code + * void connect_handler(const boost::asio::error& error) + * { + * if (!error) + * { + * // Connect succeeded. + * } + * } + * + * ... + * + * boost::asio::stream_socket socket(demuxer); + * boost::asio::ipv4::tcp::endpoint endpoint(12345, "1.2.3.4"); + * socket.async_connect(endpoint, connect_handler); + * @endcode + */ + template + void async_connect(const Endpoint& peer_endpoint, Handler handler) + { + service_.async_connect(impl_, peer_endpoint, handler); + } + + /// Set an option on the socket. + /** + * This function is used to set an option on the socket. + * + * @param option The new option value to be set on the socket. + * + * @throws boost::asio::error Thrown on failure. + * + * @sa Socket_Option @n + * boost::asio::socket_base::keep_alive @n + * boost::asio::socket_base::linger @n + * boost::asio::socket_base::send_buffer_size @n + * boost::asio::socket_base::send_low_watermark @n + * boost::asio::socket_base::receive_buffer_size @n + * boost::asio::socket_base::receive_low_watermark @n + * boost::asio::ipv4::tcp::no_delay + * + * @par Example: + * Setting the IPPROTO_TCP/TCP_NODELAY option: + * @code + * boost::asio::stream_socket socket(demuxer); + * ... + * boost::asio::ipv4::tcp::no_delay option(true); + * socket.set_option(option); + * @endcode + */ + template + void set_option(const Socket_Option& option) + { + service_.set_option(impl_, option, throw_error()); + } + + /// Set an option on the socket. + /** + * This function is used to set an option on the socket. + * + * @param option The new option value to be set on the socket. + * + * @param error_handler The handler to be called when an error occurs. Copies + * will be made of the handler as required. The function signature of the + * handler must be: + * @code void error_handler( + * const boost::asio::error& error // Result of operation + * ); @endcode + * + * @sa Socket_Option @n + * boost::asio::socket_base::keep_alive @n + * boost::asio::socket_base::linger @n + * boost::asio::socket_base::send_buffer_size @n + * boost::asio::socket_base::send_low_watermark @n + * boost::asio::socket_base::receive_buffer_size @n + * boost::asio::socket_base::receive_low_watermark @n + * boost::asio::ipv4::tcp::no_delay + * + * @par Example: + * Setting the IPPROTO_TCP/TCP_NODELAY option: + * @code + * boost::asio::stream_socket socket(demuxer); + * ... + * boost::asio::ipv4::tcp::no_delay option(true); + * boost::asio::error error; + * socket.set_option(option, boost::asio::assign_error(error)); + * if (error) + * { + * // An error occurred. + * } + * @endcode + */ + template + void set_option(const Socket_Option& option, Error_Handler error_handler) + { + service_.set_option(impl_, option, error_handler); + } + + /// Get an option from the socket. + /** + * This function is used to get the current value of an option on the socket. + * + * @param option The option value to be obtained from the socket. + * + * @throws boost::asio::error Thrown on failure. + * + * @sa Socket_Option @n + * boost::asio::socket_base::keep_alive @n + * boost::asio::socket_base::linger @n + * boost::asio::socket_base::send_buffer_size @n + * boost::asio::socket_base::send_low_watermark @n + * boost::asio::socket_base::receive_buffer_size @n + * boost::asio::socket_base::receive_low_watermark @n + * boost::asio::ipv4::tcp::no_delay + * + * @par Example: + * Getting the value of the SOL_SOCKET/SO_KEEPALIVE option: + * @code + * boost::asio::stream_socket socket(demuxer); + * ... + * boost::asio::stream_socket::keep_alive option; + * socket.get_option(option); + * bool is_set = option.get(); + * @endcode + */ + template + void get_option(Socket_Option& option) const + { + service_.get_option(impl_, option, throw_error()); + } + + /// Get an option from the socket. + /** + * This function is used to get the current value of an option on the socket. + * + * @param option The option value to be obtained from the socket. + * + * @param error_handler The handler to be called when an error occurs. Copies + * will be made of the handler as required. The function signature of the + * handler must be: + * @code void error_handler( + * const boost::asio::error& error // Result of operation + * ); @endcode + * + * @sa Socket_Option @n + * boost::asio::socket_base::keep_alive @n + * boost::asio::socket_base::linger @n + * boost::asio::socket_base::send_buffer_size @n + * boost::asio::socket_base::send_low_watermark @n + * boost::asio::socket_base::receive_buffer_size @n + * boost::asio::socket_base::receive_low_watermark @n + * boost::asio::ipv4::tcp::no_delay + * + * @par Example: + * Getting the value of the SOL_SOCKET/SO_KEEPALIVE option: + * @code + * boost::asio::stream_socket socket(demuxer); + * ... + * boost::asio::stream_socket::keep_alive option; + * boost::asio::error error; + * socket.get_option(option, boost::asio::assign_error(error)); + * if (error) + * { + * // An error occurred. + * } + * bool is_set = option.get(); + * @endcode + */ + template + void get_option(Socket_Option& option, Error_Handler error_handler) const + { + service_.get_option(impl_, option, error_handler); + } + + /// Perform an IO control command on the socket. + /** + * This function is used to execute an IO control command on the socket. + * + * @param command The IO control command to be performed on the socket. + * + * @throws boost::asio::error Thrown on failure. + * + * @sa IO_Control_Command @n + * boost::asio::socket_base::bytes_readable @n + * boost::asio::socket_base::non_blocking_io + * + * @par Example: + * Getting the number of bytes ready to read: + * @code + * boost::asio::stream_socket socket(demuxer); + * ... + * boost::asio::stream_socket::bytes_readable command; + * socket.io_control(command); + * std::size_t bytes_readable = command.get(); + * @endcode + */ + template + void io_control(IO_Control_Command& command) + { + service_.io_control(impl_, command, throw_error()); + } + + /// Perform an IO control command on the socket. + /** + * This function is used to execute an IO control command on the socket. + * + * @param command The IO control command to be performed on the socket. + * + * @param error_handler The handler to be called when an error occurs. Copies + * will be made of the handler as required. The function signature of the + * handler must be: + * @code void error_handler( + * const boost::asio::error& error // Result of operation + * ); @endcode + * + * @sa IO_Control_Command @n + * boost::asio::socket_base::bytes_readable @n + * boost::asio::socket_base::non_blocking_io + * + * @par Example: + * Getting the number of bytes ready to read: + * @code + * boost::asio::stream_socket socket(demuxer); + * ... + * boost::asio::stream_socket::bytes_readable command; + * boost::asio::error error; + * socket.io_control(command, boost::asio::assign_error(error)); + * if (error) + * { + * // An error occurred. + * } + * std::size_t bytes_readable = command.get(); + * @endcode + */ + template + void io_control(IO_Control_Command& command, Error_Handler error_handler) + { + service_.io_control(impl_, command, error_handler); + } + + /// Get the local endpoint of the socket. + /** + * This function is used to obtain the locally bound endpoint of the socket. + * + * @param endpoint An endpoint object that receives the local endpoint of the + * socket. + * + * @throws boost::asio::error Thrown on failure. + * + * @par Example: + * @code + * boost::asio::stream_socket socket(demuxer); + * ... + * boost::asio::ipv4::tcp::endpoint endpoint; + * socket.get_local_endpoint(endpoint); + * @endcode + */ + template + void get_local_endpoint(Endpoint& endpoint) const + { + service_.get_local_endpoint(impl_, endpoint, throw_error()); + } + + /// Get the local endpoint of the socket. + /** + * This function is used to obtain the locally bound endpoint of the socket. + * + * @param endpoint An endpoint object that receives the local endpoint of the + * socket. + * + * @param error_handler The handler to be called when an error occurs. Copies + * will be made of the handler as required. The function signature of the + * handler must be: + * @code void error_handler( + * const boost::asio::error& error // Result of operation + * ); @endcode + * + * @par Example: + * @code + * boost::asio::stream_socket socket(demuxer); + * ... + * boost::asio::ipv4::tcp::endpoint endpoint; + * boost::asio::error error; + * socket.get_local_endpoint(endpoint, boost::asio::assign_error(error)); + * if (error) + * { + * // An error occurred. + * } + * @endcode + */ + template + void get_local_endpoint(Endpoint& endpoint, + Error_Handler error_handler) const + { + service_.get_local_endpoint(impl_, endpoint, error_handler); + } + + /// Get the remote endpoint of the socket. + /** + * This function is used to obtain the remote endpoint of the socket. + * + * @param endpoint An endpoint object that receives the remote endpoint of + * the socket. + * + * @throws boost::asio::error Thrown on failure. + * + * @par Example: + * @code + * boost::asio::stream_socket socket(demuxer); + * ... + * boost::asio::ipv4::tcp::endpoint endpoint; + * socket.get_remote_endpoint(endpoint); + * @endcode + */ + template + void get_remote_endpoint(Endpoint& endpoint) const + { + service_.get_remote_endpoint(impl_, endpoint, throw_error()); + } + + /// Get the remote endpoint of the socket. + /** + * This function is used to obtain the remote endpoint of the socket. + * + * @param endpoint An endpoint object that receives the remote endpoint of + * the socket. + * + * @param error_handler The handler to be called when an error occurs. Copies + * will be made of the handler as required. The function signature of the + * handler must be: + * @code void error_handler( + * const boost::asio::error& error // Result of operation + * ); @endcode + * + * @par Example: + * @code + * boost::asio::stream_socket socket(demuxer); + * ... + * boost::asio::ipv4::tcp::endpoint endpoint; + * boost::asio::error error; + * socket.get_remote_endpoint(endpoint, boost::asio::assign_error(error)); + * if (error) + * { + * // An error occurred. + * } + * @endcode + */ + template + void get_remote_endpoint(Endpoint& endpoint, + Error_Handler error_handler) const + { + service_.get_remote_endpoint(impl_, endpoint, error_handler); + } + + /// Disable sends or receives on the socket. + /** + * This function is used to disable send operations, receive operations, or + * both. + * + * @param what Determines what types of operation will no longer be allowed. + * + * @throws boost::asio::error Thrown on failure. + * + * @par Example: + * Shutting down the send side of the socket: + * @code + * boost::asio::stream_socket socket(demuxer); + * ... + * socket.shutdown(boost::asio::stream_socket::shutdown_send); + * @endcode + */ + void shutdown(shutdown_type what) + { + service_.shutdown(impl_, what, throw_error()); + } + + /// Disable sends or receives on the socket. + /** + * This function is used to disable send operations, receive operations, or + * both. + * + * @param what Determines what types of operation will no longer be allowed. + * + * @param error_handler The handler to be called when an error occurs. Copies + * will be made of the handler as required. The function signature of the + * handler must be: + * @code void error_handler( + * const boost::asio::error& error // Result of operation + * ); @endcode + * + * @par Example: + * Shutting down the send side of the socket: + * @code + * boost::asio::stream_socket socket(demuxer); + * ... + * boost::asio::error error; + * socket.shutdown(boost::asio::stream_socket::shutdown_send, + * boost::asio::assign_error(error)); + * if (error) + * { + * // An error occurred. + * } + * @endcode + */ + template + void shutdown(shutdown_type what, Error_Handler error_handler) + { + service_.shutdown(impl_, what, error_handler); + } + + /// Send some data on the socket. + /** + * This function is used to send data on the stream socket. The function + * call will block until one or more bytes of the data has been sent + * successfully, or an until error occurs. + * + * @param buffers One or more data buffers to be sent on the socket. + * + * @param flags Flags specifying how the send call is to be made. + * + * @returns The number of bytes sent. + * + * @throws boost::asio::error Thrown on failure. + * + * @note The send operation may not transmit all of the data to the peer. + * Consider using the @ref write function if you need to ensure that all data + * is written before the blocking operation completes. + * + * @par Example: + * To send a single data buffer use the @ref buffer function as follows: + * @code + * socket.send(boost::asio::buffer(data, size), 0); + * @endcode + * See the @ref buffer documentation for information on sending multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + std::size_t send(const Const_Buffers& buffers, message_flags flags) + { + return service_.send(impl_, buffers, flags, throw_error()); + } + + /// Send some data on the socket. + /** + * This function is used to send data on the stream socket. The function + * call will block until one or more bytes of the data has been sent + * successfully, or an until error occurs. + * + * @param buffers One or more data buffers to be sent on the socket. + * + * @param flags Flags specifying how the send call is to be made. + * + * @param error_handler The handler to be called when an error occurs. Copies + * will be made of the handler as required. The function signature of the + * handler must be: + * @code void error_handler( + * const boost::asio::error& error // Result of operation. + * ); @endcode + * + * @returns The number of bytes sent. Returns 0 if an error occurred and the + * error handler did not throw an exception. + * + * @note The send operation may not transmit all of the data to the peer. + * Consider using the @ref write function if you need to ensure that all data + * is written before the blocking operation completes. + */ + template + std::size_t send(const Const_Buffers& buffers, message_flags flags, + Error_Handler error_handler) + { + return service_.send(impl_, buffers, flags, error_handler); + } + + /// Start an asynchronous send. + /** + * This function is used to asynchronously send data on the stream socket. + * The function call always returns immediately. + * + * @param buffers One or more data buffers to be sent on the socket. Although + * the buffers object may be copied as necessary, ownership of the underlying + * memory blocks is retained by the caller, which must guarantee that they + * remain valid until the handler is called. + * + * @param flags Flags specifying how the send call is to be made. + * + * @param handler The handler to be called when the send operation completes. + * Copies will be made of the handler as required. The function signature of + * the handler must be: + * @code void handler( + * const boost::asio::error& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes sent. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * boost::asio::demuxer::post(). + * + * @note The send operation may not transmit all of the data to the peer. + * Consider using the @ref async_write function if you need to ensure that all + * data is written before the asynchronous operation completes. + * + * @par Example: + * To send a single data buffer use the @ref buffer function as follows: + * @code + * socket.async_send(boost::asio::buffer(data, size), 0, handler); + * @endcode + * See the @ref buffer documentation for information on sending multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + void async_send(const Const_Buffers& buffers, message_flags flags, + Handler handler) + { + service_.async_send(impl_, buffers, flags, handler); + } + + /// Receive some data on the socket. + /** + * This function is used to receive data on the stream socket. The function + * call will block until one or more bytes of data has been received + * successfully, or until an error occurs. + * + * @param buffers One or more buffers into which the data will be received. + * + * @param flags Flags specifying how the receive call is to be made. + * + * @returns The number of bytes received. + * + * @throws boost::asio::error Thrown on failure. An error code of + * boost::asio::error::eof indicates that the connection was closed by the + * peer. + * + * @note The receive operation may not receive all of the requested number of + * bytes. Consider using the @ref read function if you need to ensure that the + * requested amount of data is read before the blocking operation completes. + * + * @par Example: + * To receive into a single data buffer use the @ref buffer function as + * follows: + * @code + * socket.receive(boost::asio::buffer(data, size), 0); + * @endcode + * See the @ref buffer documentation for information on receiving into + * multiple buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + std::size_t receive(const Mutable_Buffers& buffers, message_flags flags) + { + return service_.receive(impl_, buffers, flags, throw_error()); + } + + /// Receive some data on a connected socket. + /** + * This function is used to receive data on the stream socket. The function + * call will block until one or more bytes of data has been received + * successfully, or until an error occurs. + * + * @param buffers One or more buffers into which the data will be received. + * + * @param flags Flags specifying how the receive call is to be made. + * + * @param error_handler The handler to be called when an error occurs. Copies + * will be made of the handler as required. The function signature of the + * handler must be: + * @code void error_handler( + * const boost::asio::error& error // Result of operation + * ); @endcode + * + * @returns The number of bytes received. Returns 0 if an error occurred and + * the error handler did not throw an exception. + * + * @note The receive operation may not receive all of the requested number of + * bytes. Consider using the @ref read function if you need to ensure that the + * requested amount of data is read before the blocking operation completes. + */ + template + std::size_t receive(const Mutable_Buffers& buffers, message_flags flags, + Error_Handler error_handler) + { + return service_.receive(impl_, buffers, flags, error_handler); + } + + /// Start an asynchronous receive. + /** + * This function is used to asynchronously receive data from the stream + * socket. The function call always returns immediately. + * + * @param buffers One or more buffers into which the data will be received. + * Although the buffers object may be copied as necessary, ownership of the + * underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the handler is called. + * + * @param flags Flags specifying how the receive call is to be made. + * + * @param handler The handler to be called when the receive operation + * completes. Copies will be made of the handler as required. The function + * signature of the handler must be: + * @code void handler( + * const boost::asio::error& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes received. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * boost::asio::demuxer::post(). + * + * @note The receive operation may not receive all of the requested number of + * bytes. Consider using the @ref async_read function if you need to ensure + * that the requested amount of data is received before the asynchronous + * operation completes. + * + * @par Example: + * To receive into a single data buffer use the @ref buffer function as + * follows: + * @code + * socket.async_receive(boost::asio::buffer(data, size), 0, handler); + * @endcode + * See the @ref buffer documentation for information on receiving into + * multiple buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + void async_receive(const Mutable_Buffers& buffers, message_flags flags, + Handler handler) + { + service_.async_receive(impl_, buffers, flags, handler); + } + + /// Write some data to the socket. + /** + * This function is used to write data to the stream socket. The function call + * will block until one or more bytes of the data has been written + * successfully, or until an error occurs. + * + * @param buffers One or more data buffers to be written to the socket. + * + * @returns The number of bytes written. + * + * @throws boost::asio::error Thrown on failure. An error code of + * boost::asio::error::eof indicates that the connection was closed by the + * peer. + * + * @note The write_some operation may not transmit all of the data to the + * peer. Consider using the @ref write function if you need to ensure that + * all data is written before the blocking operation completes. + * + * @par Example: + * To write a single data buffer use the @ref buffer function as follows: + * @code + * socket.write_some(boost::asio::buffer(data, size)); + * @endcode + * See the @ref buffer documentation for information on writing multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + std::size_t write_some(const Const_Buffers& buffers) + { + return service_.send(impl_, buffers, 0, throw_error()); + } + + /// Write some data to the socket. + /** + * This function is used to write data to the stream socket. The function call + * will block until one or more bytes of the data has been written + * successfully, or until an error occurs. + * + * @param buffers One or more data buffers to be written to the socket. + * + * @param error_handler The handler to be called when an error occurs. Copies + * will be made of the handler as required. The function signature of the + * handler must be: + * @code void error_handler( + * const boost::asio::error& error // Result of operation. + * ); @endcode + * + * @returns The number of bytes written. Returns 0 if an error occurred and + * the error handler did not throw an exception. + * + * @note The write_some operation may not transmit all of the data to the + * peer. Consider using the @ref write function if you need to ensure that + * all data is written before the blocking operation completes. + */ + template + std::size_t write_some(const Const_Buffers& buffers, + Error_Handler error_handler) + { + return service_.send(impl_, buffers, 0, error_handler); + } + + /// Start an asynchronous write. + /** + * This function is used to asynchronously write data to the stream socket. + * The function call always returns immediately. + * + * @param buffers One or more data buffers to be written to the socket. + * Although the buffers object may be copied as necessary, ownership of the + * underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the handler is called. + * + * @param handler The handler to be called when the write operation completes. + * Copies will be made of the handler as required. The function signature of + * the handler must be: + * @code void handler( + * const boost::asio::error& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes written. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * boost::asio::demuxer::post(). + * + * @note The write operation may not transmit all of the data to the peer. + * Consider using the @ref async_write function if you need to ensure that all + * data is written before the asynchronous operation completes. + * + * @par Example: + * To write a single data buffer use the @ref buffer function as follows: + * @code + * socket.async_write_some(boost::asio::buffer(data, size), handler); + * @endcode + * See the @ref buffer documentation for information on writing multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + void async_write_some(const Const_Buffers& buffers, Handler handler) + { + service_.async_send(impl_, buffers, 0, handler); + } + + /// Read some data from the socket. + /** + * This function is used to read data from the stream socket. The function + * call will block until one or more bytes of data has been read successfully, + * or until an error occurs. + * + * @param buffers One or more buffers into which the data will be read. + * + * @returns The number of bytes read. + * + * @throws boost::asio::error Thrown on failure. An error code of + * boost::asio::error::eof indicates that the connection was closed by the + * peer. + * + * @note The read_some operation may not read all of the requested number of + * bytes. Consider using the @ref read function if you need to ensure that + * the requested amount of data is read before the blocking operation + * completes. + * + * @par Example: + * To read into a single data buffer use the @ref buffer function as follows: + * @code + * socket.read_some(boost::asio::buffer(data, size)); + * @endcode + * See the @ref buffer documentation for information on reading into multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + std::size_t read_some(const Mutable_Buffers& buffers) + { + return service_.receive(impl_, buffers, 0, throw_error()); + } + + /// Read some data from the socket. + /** + * This function is used to read data from the stream socket. The function + * call will block until one or more bytes of data has been read successfully, + * or until an error occurs. + * + * @param buffers One or more buffers into which the data will be read. + * + * @param error_handler The handler to be called when an error occurs. Copies + * will be made of the handler as required. The function signature of the + * handler must be: + * @code void error_handler( + * const boost::asio::error& error // Result of operation. + * ); @endcode + * + * @returns The number of bytes read. Returns 0 if an error occurred and the + * error handler did not throw an exception. + * + * @note The read_some operation may not read all of the requested number of + * bytes. Consider using the @ref read function if you need to ensure that + * the requested amount of data is read before the blocking operation + * completes. + */ + template + std::size_t read_some(const Mutable_Buffers& buffers, + Error_Handler error_handler) + { + return service_.receive(impl_, buffers, 0, error_handler); + } + + /// Start an asynchronous read. + /** + * This function is used to asynchronously read data from the stream socket. + * The function call always returns immediately. + * + * @param buffers One or more buffers into which the data will be read. + * Although the buffers object may be copied as necessary, ownership of the + * underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the handler is called. + * + * @param handler The handler to be called when the read operation completes. + * Copies will be made of the handler as required. The function signature of + * the handler must be: + * @code void handler( + * const boost::asio::error& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes read. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * boost::asio::demuxer::post(). + * + * @note The read operation may not read all of the requested number of bytes. + * Consider using the @ref async_read function if you need to ensure that the + * requested amount of data is read before the asynchronous operation + * completes. + * + * @par Example: + * To read into a single data buffer use the @ref buffer function as follows: + * @code + * socket.async_read_some(boost::asio::buffer(data, size), handler); + * @endcode + * See the @ref buffer documentation for information on reading into multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + void async_read_some(const Mutable_Buffers& buffers, Handler handler) + { + service_.async_receive(impl_, buffers, 0, handler); + } + + /// Peek at the incoming data on the stream socket. + /** + * This function is used to peek at the incoming data on the stream socket, + * without removing it from the input queue. The function call will block + * until data has been read successfully or an error occurs. + * + * @param buffers One or more buffers into which the data will be read. + * + * @returns The number of bytes read. + * + * @throws boost::asio::error Thrown on failure. + * + * @par Example: + * To peek using a single data buffer use the @ref buffer function as + * follows: + * @code socket.peek(boost::asio::buffer(data, size)); @endcode + * See the @ref buffer documentation for information on using multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + std::size_t peek(const Mutable_Buffers& buffers) + { + return service_.receive(impl_, buffers, message_peek, throw_error()); + } + + /// Peek at the incoming data on the stream socket. + /** + * This function is used to peek at the incoming data on the stream socket, + * without removing it from the input queue. The function call will block + * until data has been read successfully or an error occurs. + * + * @param buffers One or more buffers into which the data will be read. + * + * @param error_handler The handler to be called when an error occurs. Copies + * will be made of the handler as required. The function signature of the + * handler must be: + * @code void error_handler( + * const boost::asio::error& error // Result of operation. + * ); @endcode + * + * @returns The number of bytes read. Returns 0 if an error occurred and the + * error handler did not throw an exception. + */ + template + std::size_t peek(const Mutable_Buffers& buffers, Error_Handler error_handler) + { + return service_.receive(impl_, buffers, message_peek, error_handler); + } + + /// Determine the amount of data that may be read without blocking. + /** + * This function is used to determine the amount of data, in bytes, that may + * be read from the stream socket without blocking. + * + * @returns The number of bytes of data that can be read without blocking. + * + * @throws boost::asio::error Thrown on failure. + */ + std::size_t in_avail() + { + bytes_readable command; + service_.io_control(impl_, command, throw_error()); + return command.get(); + } + + /// Determine the amount of data that may be read without blocking. + /** + * This function is used to determine the amount of data, in bytes, that may + * be read from the stream socket without blocking. + * + * @param error_handler The handler to be called when an error occurs. Copies + * will be made of the handler as required. The function signature of the + * handler must be: + * @code void error_handler( + * const boost::asio::error& error // Result of operation + * ); @endcode + * + * @returns The number of bytes of data that can be read without blocking. + */ + template + std::size_t in_avail(Error_Handler error_handler) + { + bytes_readable command; + service_.io_control(impl_, command, error_handler); + return command.get(); + } + +private: + /// The backend service implementation. + service_type& service_; + + /// The underlying native implementation. + impl_type impl_; +}; + +} // namespace asio +} // namespace boost + +#include + +#endif // BOOST_ASIO_BASIC_STREAM_SOCKET_HPP diff --git a/include/boost/asio/buffer.hpp b/include/boost/asio/buffer.hpp new file mode 100644 index 00000000..1df584cb --- /dev/null +++ b/include/boost/asio/buffer.hpp @@ -0,0 +1,625 @@ +// +// buffer.hpp +// ~~~~~~~~~~ +// +// Copyright (c) 2003-2005 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_BUFFER_HPP +#define BOOST_ASIO_BUFFER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace boost { +namespace asio { + +class mutable_buffer; +class const_buffer; + +namespace detail { +void* buffer_cast_helper(const mutable_buffer&); +const void* buffer_cast_helper(const const_buffer&); +std::size_t buffer_size_helper(const mutable_buffer&); +std::size_t buffer_size_helper(const const_buffer&); +} // namespace detail + +/// Holds a buffer that can be modified. +/** + * The const_buffer class provides a safe representation of a buffer that can be + * modified. It does not own the underlying data, and so is cheap to copy or + * assign. + */ +class mutable_buffer +{ +public: + /// Construct an empty buffer. + mutable_buffer() + : data_(0), + size_(0) + { + } + + /// Construct a buffer to represent a given memory range. + mutable_buffer(void* data, std::size_t size) + : data_(data), + size_(size) + { + } + +private: + friend void* boost::asio::detail::buffer_cast_helper( + const mutable_buffer& b); + friend std::size_t boost::asio::detail::buffer_size_helper( + const mutable_buffer& b); + + void* data_; + std::size_t size_; +}; + +namespace detail { + +inline void* buffer_cast_helper(const mutable_buffer& b) +{ + return b.data_; +} + +inline std::size_t buffer_size_helper(const mutable_buffer& b) +{ + return b.size_; +} + +} // namespace detail + +/// Cast a non-modifiable buffer to a specified pointer to POD type. +/** + * @relates mutable_buffer + */ +template +inline Pointer_To_Pod_Type buffer_cast(const mutable_buffer& b) +{ + return static_cast(detail::buffer_cast_helper(b)); +} + +/// Get the number of bytes in a non-modifiable buffer. +/** + * @relates mutable_buffer + */ +inline std::size_t buffer_size(const mutable_buffer& b) +{ + return detail::buffer_size_helper(b); +} + +/// Create a new modifiable buffer that is offset from the start of another. +/** + * @relates mutable_buffer + */ +inline mutable_buffer operator+(const mutable_buffer& b, std::size_t start) +{ + if (start > buffer_size(b)) + return mutable_buffer(); + char* new_data = buffer_cast(b) + start; + std::size_t new_size = buffer_size(b) - start; + return mutable_buffer(new_data, new_size); +} + +/// Create a new modifiable buffer that is offset from the start of another. +/** + * @relates mutable_buffer + */ +inline mutable_buffer operator+(std::size_t start, const mutable_buffer& b) +{ + if (start > buffer_size(b)) + return mutable_buffer(); + char* new_data = buffer_cast(b) + start; + std::size_t new_size = buffer_size(b) - start; + return mutable_buffer(new_data, new_size); +} + +/// Adapts a single modifiable buffer so that it meets the requirements of the +/// Mutable_Buffers concept. +class mutable_buffer_container_1 + : public mutable_buffer +{ +public: + /// The type for each element in the list of buffers. + typedef mutable_buffer value_type; + + /// A random-access iterator type that may be used to read or modify elements. + typedef mutable_buffer* iterator; + + /// A random-access iterator type that may be used to read elements. + typedef const mutable_buffer* const_iterator; + + /// Construct to represent a single modifiable buffer. + explicit mutable_buffer_container_1(const mutable_buffer& b) + : mutable_buffer(b) + { + } + + /// Get a random-access iterator to the first element. + iterator begin() + { + return this; + } + + /// Get a random-access iterator to the first element. + const_iterator begin() const + { + return this; + } + + /// Get a random-access iterator for one past the last element. + iterator end() + { + return begin() + 1; + } + + /// Get a random-access iterator for one past the last element. + const_iterator end() const + { + return begin() + 1; + } +}; + +/// Holds a buffer that cannot be modified. +/** + * The const_buffer class provides a safe representation of a buffer that cannot + * be modified. It does not own the underlying data, and so is cheap to copy or + * assign. + */ +class const_buffer +{ +public: + /// Construct an empty buffer. + const_buffer() + : data_(0), + size_(0) + { + } + + /// Construct a buffer to represent a given memory range. + const_buffer(const void* data, std::size_t size) + : data_(data), + size_(size) + { + } + + /// Construct a non-modifiable buffer from a modifiable one. + const_buffer(const mutable_buffer& b) + : data_(boost::asio::detail::buffer_cast_helper(b)), + size_(boost::asio::detail::buffer_size_helper(b)) + { + } + +private: + friend const void* boost::asio::detail::buffer_cast_helper( + const const_buffer& b); + friend std::size_t boost::asio::detail::buffer_size_helper( + const const_buffer& b); + + const void* data_; + std::size_t size_; +}; + +namespace detail { + +inline const void* buffer_cast_helper(const const_buffer& b) +{ + return b.data_; +} + +inline std::size_t buffer_size_helper(const const_buffer& b) +{ + return b.size_; +} + +} // namespace detail + +/// Cast a non-modifiable buffer to a specified pointer to POD type. +/** + * @relates const_buffer + */ +template +inline Pointer_To_Pod_Type buffer_cast(const const_buffer& b) +{ + return static_cast(detail::buffer_cast_helper(b)); +} + +/// Get the number of bytes in a non-modifiable buffer. +/** + * @relates const_buffer + */ +inline std::size_t buffer_size(const const_buffer& b) +{ + return detail::buffer_size_helper(b); +} + +/// Create a new non-modifiable buffer that is offset from the start of another. +/** + * @relates const_buffer + */ +inline const_buffer operator+(const const_buffer& b, std::size_t start) +{ + if (start > buffer_size(b)) + return const_buffer(); + const char* new_data = buffer_cast(b) + start; + std::size_t new_size = buffer_size(b) - start; + return const_buffer(new_data, new_size); +} + +/// Create a new non-modifiable buffer that is offset from the start of another. +/** + * @relates const_buffer + */ +inline const_buffer operator+(std::size_t start, const const_buffer& b) +{ + if (start > buffer_size(b)) + return const_buffer(); + const char* new_data = buffer_cast(b) + start; + std::size_t new_size = buffer_size(b) - start; + return const_buffer(new_data, new_size); +} + +/// Adapts a single non-modifiable buffer so that it meets the requirements of +/// the Const_Buffers concept. +class const_buffer_container_1 + : public const_buffer +{ +public: + /// The type for each element in the list of buffers. + typedef const_buffer value_type; + + /// A random-access iterator type that may be used to read or modify elements. + typedef const_buffer* iterator; + + /// A random-access iterator type that may be used to read elements. + typedef const const_buffer* const_iterator; + + /// Construct to represent a single non-modifiable buffer. + explicit const_buffer_container_1(const const_buffer& b) + : const_buffer(b) + { + } + + /// Get a random-access iterator to the first element. + iterator begin() + { + return this; + } + + /// Get a random-access iterator to the first element. + const_iterator begin() const + { + return this; + } + + /// Get a random-access iterator for one past the last element. + iterator end() + { + return begin() + 1; + } + + /// Get a random-access iterator for one past the last element. + const_iterator end() const + { + return begin() + 1; + } +}; + +/** @defgroup buffer boost::asio::buffer + * + * @brief The boost::asio::buffer function is used to create a buffer object to + * represent raw memory, an array of POD elements, or a vector of POD elements. + * + * The simplest use case involves reading or writing a single buffer of a + * specified size: + * + * @code sock.write(boost::asio::buffer(data, size)); @endcode + * + * In the above example, the return value of boost::asio::buffer meets the + * requirements of the Const_Buffers concept so that it may be directly passed + * to the socket's write function. A buffer created for modifiable memory also + * meets the requirements of the Mutable_Buffers concept. + * + * An individual buffer may be created from a builtin array, std::vector or + * boost::array of POD elements. This helps prevent buffer overruns by + * automatically determining the size of the buffer: + * + * @code char d1[128]; + * size_t bytes_transferred = sock.read(boost::asio::buffer(d1)); + * + * std::vector d2(128); + * bytes_transferred = sock.read(boost::asio::buffer(d2)); + * + * boost::array d3; + * bytes_transferred = sock.read(boost::asio::buffer(d3)); @endcode + * + * To read or write using multiple buffers (i.e. scatter-gather I/O), multiple + * buffer objects may be assigned into a container that supports the + * Mutable_Buffers (for read) or Const_Buffers (for write) concepts: + * + * @code + * char d1[128]; + * std::vector d2(128); + * boost::array d3; + * + * boost::array bufs1 = { + * boost::asio::buffer(d1), + * boost::asio::buffer(d2), + * boost::asio::buffer(d3) }; + * bytes_transferred = sock.read(bufs1); + * + * std::vector bufs2; + * bufs2.push_back(boost::asio::buffer(d1)); + * bufs2.push_back(boost::asio::buffer(d2)); + * bufs2.push_back(boost::asio::buffer(d3)); + * bytes_transferred = sock.write(bufs2); @endcode + */ +/*@{*/ + +/// Create a new modifiable buffer from an existing buffer. +inline mutable_buffer_container_1 buffer(const mutable_buffer& b) +{ + return mutable_buffer_container_1(b); +} + +/// Create a new modifiable buffer from an existing buffer. +inline mutable_buffer_container_1 buffer(const mutable_buffer& b, + std::size_t max_size_in_bytes) +{ + return mutable_buffer_container_1( + mutable_buffer(buffer_cast(b), + buffer_size(b) < max_size_in_bytes + ? buffer_size(b) : max_size_in_bytes)); +} + +/// Create a new non-modifiable buffer from an existing buffer. +inline const_buffer_container_1 buffer(const const_buffer& b) +{ + return const_buffer_container_1(b); +} + +/// Create a new non-modifiable buffer from an existing buffer. +inline const_buffer_container_1 buffer(const const_buffer& b, + std::size_t max_size_in_bytes) +{ + return const_buffer_container_1( + const_buffer(buffer_cast(b), + buffer_size(b) < max_size_in_bytes + ? buffer_size(b) : max_size_in_bytes)); +} + +/// Create a new modifiable buffer that represents the given memory range. +inline mutable_buffer_container_1 buffer(void* data, std::size_t size_in_bytes) +{ + return mutable_buffer_container_1(mutable_buffer(data, size_in_bytes)); +} + +/// Create a new non-modifiable buffer that represents the given memory range. +inline const_buffer_container_1 buffer(const void* data, + std::size_t size_in_bytes) +{ + return const_buffer_container_1(const_buffer(data, size_in_bytes)); +} + +/// Create a new modifiable buffer that represents the given POD array. +template +inline mutable_buffer_container_1 buffer(Pod_Type (&data)[N]) +{ + return mutable_buffer_container_1(mutable_buffer(data, N * sizeof(Pod_Type))); +} + +/// Create a new modifiable buffer that represents the given POD array. +template +inline mutable_buffer_container_1 buffer(Pod_Type (&data)[N], + std::size_t max_size_in_bytes) +{ + return mutable_buffer_container_1( + mutable_buffer(data, + N * sizeof(Pod_Type) < max_size_in_bytes + ? N * sizeof(Pod_Type) : max_size_in_bytes)); +} + +/// Create a new non-modifiable buffer that represents the given POD array. +template +inline const_buffer_container_1 buffer(const Pod_Type (&data)[N]) +{ + return const_buffer_container_1(const_buffer(data, N * sizeof(Pod_Type))); +} + +/// Create a new non-modifiable buffer that represents the given POD array. +template +inline const_buffer_container_1 buffer(const Pod_Type (&data)[N], + std::size_t max_size_in_bytes) +{ + return const_buffer_container_1( + const_buffer(data, + N * sizeof(Pod_Type) < max_size_in_bytes + ? N * sizeof(Pod_Type) : max_size_in_bytes)); +} + +/// Create a new modifiable buffer that represents the given POD array. +template +inline mutable_buffer_container_1 buffer(boost::array& data) +{ + return mutable_buffer_container_1( + mutable_buffer(data.c_array(), data.size() * sizeof(Pod_Type))); +} + +/// Create a new modifiable buffer that represents the given POD array. +template +inline mutable_buffer_container_1 buffer(boost::array& data, + std::size_t max_size_in_bytes) +{ + return mutable_buffer_container_1( + mutable_buffer(data.c_array(), + data.size() * sizeof(Pod_Type) < max_size_in_bytes + ? data.size() * sizeof(Pod_Type) : max_size_in_bytes)); +} + +/// Create a new non-modifiable buffer that represents the given POD array. +template +inline const_buffer_container_1 buffer(const boost::array& data) +{ + return const_buffer_container_1( + const_buffer(data.data(), data.size() * sizeof(Pod_Type))); +} + +/// Create a new non-modifiable buffer that represents the given POD array. +template +inline const_buffer_container_1 buffer(const boost::array& data, + std::size_t max_size_in_bytes) +{ + return const_buffer_container_1( + const_buffer(data.data(), + data.size() * sizeof(Pod_Type) < max_size_in_bytes + ? data.size() * sizeof(Pod_Type) : max_size_in_bytes)); +} + +/// Create a new non-modifiable buffer that represents the given POD array. +template +inline const_buffer_container_1 buffer(boost::array& data) +{ + return const_buffer_container_1( + const_buffer(data.data(), data.size() * sizeof(Pod_Type))); +} + +/// Create a new non-modifiable buffer that represents the given POD array. +template +inline const_buffer_container_1 buffer(boost::array& data, + std::size_t max_size_in_bytes) +{ + return const_buffer_container_1( + const_buffer(data.data(), + data.size() * sizeof(Pod_Type) < max_size_in_bytes + ? data.size() * sizeof(Pod_Type) : max_size_in_bytes)); +} + +/// Create a new modifiable buffer that represents the given POD vector. +/** + * @note The buffer is invalidated by any vector operation that would also + * invalidate iterators. + */ +template +inline mutable_buffer_container_1 buffer(std::vector& data) +{ + return mutable_buffer_container_1( + mutable_buffer(&data[0], data.size() * sizeof(Pod_Type))); +} + +/// Create a new modifiable buffer that represents the given POD vector. +/** + * @note The buffer is invalidated by any vector operation that would also + * invalidate iterators. + */ +template +inline mutable_buffer_container_1 buffer(std::vector& data, + std::size_t max_size_in_bytes) +{ + return mutable_buffer_container_1( + mutable_buffer(&data[0], + data.size() * sizeof(Pod_Type) < max_size_in_bytes + ? data.size() * sizeof(Pod_Type) : max_size_in_bytes)); +} + +/// Create a new non-modifiable buffer that represents the given POD vector. +/** + * @note The buffer is invalidated by any vector operation that would also + * invalidate iterators. + */ +template +inline const_buffer_container_1 buffer(const std::vector& data) +{ + return const_buffer_container_1( + const_buffer(&data[0], data.size() * sizeof(Pod_Type))); +} + +/// Create a new non-modifiable buffer that represents the given POD vector. +/** + * @note The buffer is invalidated by any vector operation that would also + * invalidate iterators. + */ +template +inline const_buffer_container_1 buffer(const std::vector& data, + std::size_t max_size_in_bytes) +{ + return const_buffer_container_1( + const_buffer(&data[0], + data.size() * sizeof(Pod_Type) < max_size_in_bytes + ? data.size() * sizeof(Pod_Type) : max_size_in_bytes)); +} + +/// Create a new non-modifiable buffer that represents the given POD vector. +/** + * @note The buffer is invalidated by any vector operation that would also + * invalidate iterators. + */ +template +inline const_buffer_container_1 buffer(std::vector& data) +{ + return const_buffer_container_1( + const_buffer(&data[0], data.size() * sizeof(Pod_Type))); +} + +/// Create a new non-modifiable buffer that represents the given POD vector. +/** + * @note The buffer is invalidated by any vector operation that would also + * invalidate iterators. + */ +template +inline const_buffer_container_1 buffer(std::vector& data, + std::size_t max_size_in_bytes) +{ + return const_buffer_container_1( + const_buffer(&data[0], + data.size() * sizeof(Pod_Type) < max_size_in_bytes + ? data.size() * sizeof(Pod_Type) : max_size_in_bytes)); +} + +/// Create a new non-modifiable buffer that represents the given string. +/** + * @note The buffer is invalidated by any non-const operation called on the + * given string object. + */ +inline const_buffer_container_1 buffer(const std::string& data) +{ + return const_buffer_container_1(const_buffer(data.data(), data.size())); +} + +/// Create a new non-modifiable buffer that represents the given string. +/** + * @note The buffer is invalidated by any non-const operation called on the + * given string object. + */ +inline const_buffer_container_1 buffer(const std::string& data, + std::size_t max_size_in_bytes) +{ + return const_buffer_container_1( + const_buffer(data.data(), + data.size() < max_size_in_bytes + ? data.size() : max_size_in_bytes)); +} + +/*@}*/ + +} // namespace asio +} // namespace boost + +#include + +#endif // BOOST_ASIO_BUFFER_HPP diff --git a/include/boost/asio/buffered_read_stream.hpp b/include/boost/asio/buffered_read_stream.hpp new file mode 100644 index 00000000..c4abc6a6 --- /dev/null +++ b/include/boost/asio/buffered_read_stream.hpp @@ -0,0 +1,410 @@ +// +// buffered_read_stream.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2005 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_BUFFERED_READ_STREAM_HPP +#define BOOST_ASIO_BUFFERED_READ_STREAM_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace boost { +namespace asio { + +/// Adds buffering to the read-related operations of a stream. +/** + * The buffered_read_stream class template can be used to add buffering to the + * synchronous and asynchronous read operations of a stream. + * + * @par Thread Safety: + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Unsafe. + * + * @par Concepts: + * Async_Object, Async_Read_Stream, Async_Write_Stream, Error_Source, Stream, + * Sync_Read_Stream, Sync_Write_Stream. + */ +template +class buffered_read_stream + : private noncopyable +{ +public: + /// The type of the next layer. + typedef typename boost::remove_reference::type next_layer_type; + + /// The type of the lowest layer. + typedef typename next_layer_type::lowest_layer_type lowest_layer_type; + + /// The demuxer type for this asynchronous type. + typedef typename next_layer_type::demuxer_type demuxer_type; + + /// The type used for reporting errors. + typedef typename next_layer_type::error_type error_type; + +#if defined(GENERATING_DOCUMENTATION) + /// The default buffer size. + static const std::size_t default_buffer_size = implementation_defined; +#else + BOOST_STATIC_CONSTANT(std::size_t, default_buffer_size = 1024); +#endif + + /// Construct, passing the specified demuxer to initialise the next layer. + template + explicit buffered_read_stream(Arg& a) + : next_layer_(a), + storage_(default_buffer_size) + { + } + + /// Construct, passing the specified demuxer to initialise the next layer. + template + buffered_read_stream(Arg& a, std::size_t buffer_size) + : next_layer_(a), + storage_(buffer_size) + { + } + + /// Get a reference to the next layer. + next_layer_type& next_layer() + { + return next_layer_; + } + + /// Get a reference to the lowest layer. + lowest_layer_type& lowest_layer() + { + return next_layer_.lowest_layer(); + } + + /// Get the demuxer associated with the asynchronous object. + demuxer_type& demuxer() + { + return next_layer_.demuxer(); + } + + /// Close the stream. + void close() + { + next_layer_.close(); + } + + /// Close the stream. + template + void close(Error_Handler error_handler) + { + next_layer_.close(error_handler); + } + + /// Write the given data to the stream. Returns the number of bytes written. + /// Throws an exception on failure. + template + std::size_t write_some(const Const_Buffers& buffers) + { + return next_layer_.write_some(buffers); + } + + /// Write the given data to the stream. Returns the number of bytes written, + /// or 0 if an error occurred and the error handler did not throw. + template + std::size_t write_some(const Const_Buffers& buffers, + Error_Handler error_handler) + { + return next_layer_.write_some(buffers, error_handler); + } + + /// Start an asynchronous write. The data being written must be valid for the + /// lifetime of the asynchronous operation. + template + void async_write_some(const Const_Buffers& buffers, Handler handler) + { + next_layer_.async_write_some(buffers, handler); + } + + /// Fill the buffer with some data. Returns the number of bytes placed in the + /// buffer as a result of the operation. Throws an exception on failure. + std::size_t fill() + { + detail::buffer_resize_guard + resize_guard(storage_); + std::size_t previous_size = storage_.size(); + storage_.resize(storage_.capacity()); + storage_.resize(previous_size + next_layer_.read_some(buffer( + storage_.data() + previous_size, + storage_.size() - previous_size))); + resize_guard.commit(); + return storage_.size() - previous_size; + } + + /// Fill the buffer with some data. Returns the number of bytes placed in the + /// buffer as a result of the operation, or 0 if an error occurred and the + /// error handler did not throw. + template + std::size_t fill(Error_Handler error_handler) + { + detail::buffer_resize_guard + resize_guard(storage_); + std::size_t previous_size = storage_.size(); + storage_.resize(storage_.capacity()); + storage_.resize(previous_size + next_layer_.read_some(buffer( + storage_.data() + previous_size, + storage_.size() - previous_size), + error_handler)); + resize_guard.commit(); + return storage_.size() - previous_size; + } + + template + class fill_handler + { + public: + fill_handler(demuxer_type& demuxer, + detail::buffered_stream_storage& storage, + std::size_t previous_size, Handler handler) + : demuxer_(demuxer), + storage_(storage), + previous_size_(previous_size), + handler_(handler) + { + } + + template + void operator()(const Error& e, std::size_t bytes_transferred) + { + storage_.resize(previous_size_ + bytes_transferred); + demuxer_.dispatch(detail::bind_handler(handler_, e, bytes_transferred)); + } + + private: + demuxer_type& demuxer_; + detail::buffered_stream_storage& storage_; + std::size_t previous_size_; + Handler handler_; + }; + + /// Start an asynchronous fill. + template + void async_fill(Handler handler) + { + std::size_t previous_size = storage_.size(); + storage_.resize(storage_.capacity()); + next_layer_.async_read_some( + buffer( + storage_.data() + previous_size, + storage_.size() - previous_size), + fill_handler(demuxer(), storage_, previous_size, handler)); + } + + /// Read some data from the stream. Returns the number of bytes read. Throws + /// an exception on failure. + template + std::size_t read_some(const Mutable_Buffers& buffers) + { + if (storage_.empty()) + fill(); + return copy(buffers); + } + + /// Read some data from the stream. Returns the number of bytes read or 0 if + /// an error occurred and the error handler did not throw an exception. + template + std::size_t read_some(const Mutable_Buffers& buffers, + Error_Handler error_handler) + { + if (storage_.empty() && !fill(error_handler)) + return 0; + return copy(buffers); + } + + template + class read_some_handler + { + public: + read_some_handler(demuxer_type& demuxer, + detail::buffered_stream_storage& storage, + const Mutable_Buffers& buffers, Handler handler) + : demuxer_(demuxer), + storage_(storage), + buffers_(buffers), + handler_(handler) + { + } + + void operator()(const error_type& e, std::size_t bytes_transferred) + { + if (e || storage_.empty()) + { + std::size_t length = 0; + demuxer_.dispatch(detail::bind_handler(handler_, e, length)); + } + else + { + using namespace std; // For memcpy. + + std::size_t bytes_avail = storage_.size(); + std::size_t bytes_copied = 0; + + typename Mutable_Buffers::const_iterator iter = buffers_.begin(); + typename Mutable_Buffers::const_iterator end = buffers_.end(); + for (; iter != end && bytes_avail > 0; ++iter) + { + std::size_t max_length = buffer_size(*iter); + std::size_t length = (max_length < bytes_avail) + ? max_length : bytes_avail; + memcpy(buffer_cast(*iter), + storage_.data() + bytes_copied, length); + bytes_copied += length; + bytes_avail -= length; + } + + storage_.consume(bytes_copied); + demuxer_.dispatch(detail::bind_handler(handler_, e, bytes_copied)); + } + } + + private: + demuxer_type& demuxer_; + detail::buffered_stream_storage& storage_; + Mutable_Buffers buffers_; + Handler handler_; + }; + + /// Start an asynchronous read. The buffer into which the data will be read + /// must be valid for the lifetime of the asynchronous operation. + template + void async_read_some(const Mutable_Buffers& buffers, Handler handler) + { + if (storage_.empty()) + { + async_fill(read_some_handler( + demuxer(), storage_, buffers, handler)); + } + else + { + std::size_t length = copy(buffers); + demuxer().post(detail::bind_handler(handler, 0, length)); + } + } + + /// Peek at the incoming data on the stream. Returns the number of bytes read. + /// Throws an exception on failure. + template + std::size_t peek(const Mutable_Buffers& buffers) + { + if (storage_.empty()) + fill(); + return peek_copy(buffers); + } + + /// Peek at the incoming data on the stream. Returns the number of bytes read, + /// or 0 if an error occurred and the error handler did not throw. + template + std::size_t peek(const Mutable_Buffers& buffers, Error_Handler error_handler) + { + if (storage_.empty() && !fill(error_handler)) + return 0; + return peek_copy(buffers); + } + + /// Determine the amount of data that may be read without blocking. + std::size_t in_avail() + { + return storage_.size(); + } + + /// Determine the amount of data that may be read without blocking. + template + std::size_t in_avail(Error_Handler error_handler) + { + return storage_.size(); + } + +private: + /// Copy data out of the internal buffer to the specified target buffer. + /// Returns the number of bytes copied. + template + std::size_t copy(const Mutable_Buffers& buffers) + { + using namespace std; // For memcpy. + + std::size_t bytes_avail = storage_.size(); + std::size_t bytes_copied = 0; + + typename Mutable_Buffers::const_iterator iter = buffers.begin(); + typename Mutable_Buffers::const_iterator end = buffers.end(); + for (; iter != end && bytes_avail > 0; ++iter) + { + std::size_t max_length = buffer_size(*iter); + std::size_t length = (max_length < bytes_avail) + ? max_length : bytes_avail; + memcpy(buffer_cast(*iter), storage_.data() + bytes_copied, length); + bytes_copied += length; + bytes_avail -= length; + } + + storage_.consume(bytes_copied); + return bytes_copied; + } + + /// Copy data from the internal buffer to the specified target buffer, without + /// removing the data from the internal buffer. Returns the number of bytes + /// copied. + template + std::size_t peek_copy(const Mutable_Buffers& buffers) + { + using namespace std; // For memcpy. + + std::size_t bytes_avail = storage_.size(); + std::size_t bytes_copied = 0; + + typename Mutable_Buffers::const_iterator iter = buffers.begin(); + typename Mutable_Buffers::const_iterator end = buffers.end(); + for (; iter != end && bytes_avail > 0; ++iter) + { + std::size_t max_length = buffer_size(*iter); + std::size_t length = (max_length < bytes_avail) + ? max_length : bytes_avail; + memcpy(buffer_cast(*iter), storage_.data() + bytes_copied, length); + bytes_copied += length; + bytes_avail -= length; + } + + return bytes_copied; + } + + /// The next layer. + Stream next_layer_; + + // The data in the buffer. + detail::buffered_stream_storage storage_; +}; + +} // namespace asio +} // namespace boost + +#include + +#endif // BOOST_ASIO_BUFFERED_READ_STREAM_HPP diff --git a/include/boost/asio/buffered_read_stream_fwd.hpp b/include/boost/asio/buffered_read_stream_fwd.hpp new file mode 100644 index 00000000..ecae3535 --- /dev/null +++ b/include/boost/asio/buffered_read_stream_fwd.hpp @@ -0,0 +1,31 @@ +// +// buffered_read_stream_fwd.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2005 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_BUFFERED_READ_STREAM_FWD_HPP +#define BOOST_ASIO_BUFFERED_READ_STREAM_FWD_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include + +namespace boost { +namespace asio { + +template +class buffered_read_stream; + +} // namespace asio +} // namespace boost + +#include + +#endif // BOOST_ASIO_BUFFERED_READ_STREAM_FWD_HPP diff --git a/include/boost/asio/buffered_stream.hpp b/include/boost/asio/buffered_stream.hpp new file mode 100644 index 00000000..77e0c1f5 --- /dev/null +++ b/include/boost/asio/buffered_stream.hpp @@ -0,0 +1,253 @@ +// +// buffered_stream.hpp +// ~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2005 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_BUFFERED_STREAM_HPP +#define BOOST_ASIO_BUFFERED_STREAM_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +namespace boost { +namespace asio { + +/// Adds buffering to the read- and write-related operations of a stream. +/** + * The buffered_stream class template can be used to add buffering to the + * synchronous and asynchronous read and write operations of a stream. + * + * @par Thread Safety: + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Unsafe. + * + * @par Concepts: + * Async_Object, Async_Read_Stream, Async_Write_Stream, Error_Source, Stream, + * Sync_Read_Stream, Sync_Write_Stream. + */ +template +class buffered_stream + : private noncopyable +{ +public: + /// The type of the next layer. + typedef typename boost::remove_reference::type next_layer_type; + + /// The type of the lowest layer. + typedef typename next_layer_type::lowest_layer_type lowest_layer_type; + + /// The demuxer type for this asynchronous type. + typedef typename next_layer_type::demuxer_type demuxer_type; + + /// The type used for reporting errors. + typedef typename next_layer_type::error_type error_type; + + /// Construct, passing the specified argument to initialise the next layer. + template + explicit buffered_stream(Arg& a) + : inner_stream_impl_(a), + stream_impl_(inner_stream_impl_) + { + } + + /// Construct, passing the specified argument to initialise the next layer. + template + explicit buffered_stream(Arg& a, std::size_t read_buffer_size, + std::size_t write_buffer_size) + : inner_stream_impl_(a, write_buffer_size), + stream_impl_(inner_stream_impl_, read_buffer_size) + { + } + + /// Get a reference to the next layer. + next_layer_type& next_layer() + { + return stream_impl_.next_layer().next_layer(); + } + + /// Get a reference to the lowest layer. + lowest_layer_type& lowest_layer() + { + return stream_impl_.lowest_layer(); + } + + /// Get the demuxer associated with the asynchronous object. + demuxer_type& demuxer() + { + return stream_impl_.demuxer(); + } + + /// Close the stream. + void close() + { + stream_impl_.close(); + } + + /// Close the stream. + template + void close(Error_Handler error_handler) + { + stream_impl_.close(error_handler); + } + + /// Flush all data from the buffer to the next layer. Returns the number of + /// bytes written to the next layer on the last write operation. Throws an + /// exception on failure. + std::size_t flush() + { + return stream_impl_.next_layer().flush(); + } + + /// Flush all data from the buffer to the next layer. Returns the number of + /// bytes written to the next layer on the last write operation, or 0 if an + /// error occurred and the error handler did not throw. + template + std::size_t flush(Error_Handler error_handler) + { + return stream_impl_.next_layer().flush(error_handler); + } + + /// Start an asynchronous flush. + template + void async_flush(Handler handler) + { + return stream_impl_.next_layer().async_flush(handler); + } + + /// Write the given data to the stream. Returns the number of bytes written. + /// Throws an exception on failure. + template + std::size_t write_some(const Const_Buffers& buffers) + { + return stream_impl_.write_some(buffers); + } + + /// Write the given data to the stream. Returns the number of bytes written, + /// or 0 if an error occurred and the error handler did not throw. + template + std::size_t write_some(const Const_Buffers& buffers, + Error_Handler error_handler) + { + return stream_impl_.write_some(buffers, error_handler); + } + + /// Start an asynchronous write. The data being written must be valid for the + /// lifetime of the asynchronous operation. + template + void async_write_some(const Const_Buffers& buffers, Handler handler) + { + stream_impl_.async_write_some(buffers, handler); + } + + /// Fill the buffer with some data. Returns the number of bytes placed in the + /// buffer as a result of the operation. Throws an exception on failure. + std::size_t fill() + { + return stream_impl_.fill(); + } + + /// Fill the buffer with some data. Returns the number of bytes placed in the + /// buffer as a result of the operation, or 0 if an error occurred and the + /// error handler did not throw. + template + std::size_t fill(Error_Handler error_handler) + { + return stream_impl_.fill(error_handler); + } + + /// Start an asynchronous fill. + template + void async_fill(Handler handler) + { + stream_impl_.async_fill(handler); + } + + /// Read some data from the stream. Returns the number of bytes read. Throws + /// an exception on failure. + template + std::size_t read_some(const Mutable_Buffers& buffers) + { + return stream_impl_.read_some(buffers); + } + + /// Read some data from the stream. Returns the number of bytes read or 0 if + /// an error occurred and the error handler did not throw an exception. + template + std::size_t read_some(const Mutable_Buffers& buffers, + Error_Handler error_handler) + { + return stream_impl_.read_some(buffers, error_handler); + } + + /// Start an asynchronous read. The buffer into which the data will be read + /// must be valid for the lifetime of the asynchronous operation. + template + void async_read_some(const Mutable_Buffers& buffers, Handler handler) + { + stream_impl_.async_read_some(buffers, handler); + } + + /// Peek at the incoming data on the stream. Returns the number of bytes read. + /// Throws an exception on failure. + template + std::size_t peek(const Mutable_Buffers& buffers) + { + return stream_impl_.peek(buffers); + } + + /// Peek at the incoming data on the stream. Returns the number of bytes read, + /// or 0 if an error occurred and the error handler did not throw. + template + std::size_t peek(const Mutable_Buffers& buffers, Error_Handler error_handler) + { + return stream_impl_.peek(buffers, error_handler); + } + + /// Determine the amount of data that may be read without blocking. + std::size_t in_avail() + { + return stream_impl_.in_avail(); + } + + /// Determine the amount of data that may be read without blocking. + template + std::size_t in_avail(Error_Handler error_handler) + { + return stream_impl_.in_avail(error_handler); + } + +private: + // The buffered write stream. + typedef buffered_write_stream write_stream_type; + write_stream_type inner_stream_impl_; + + // The buffered read stream. + typedef buffered_read_stream read_stream_type; + read_stream_type stream_impl_; +}; + +} // namespace asio +} // namespace boost + +#include + +#endif // BOOST_ASIO_BUFFERED_STREAM_HPP diff --git a/include/boost/asio/buffered_stream_fwd.hpp b/include/boost/asio/buffered_stream_fwd.hpp new file mode 100644 index 00000000..490907f9 --- /dev/null +++ b/include/boost/asio/buffered_stream_fwd.hpp @@ -0,0 +1,31 @@ +// +// buffered_stream_fwd.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2005 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_BUFFERED_STREAM_FWD_HPP +#define BOOST_ASIO_BUFFERED_STREAM_FWD_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include + +namespace boost { +namespace asio { + +template +class buffered_stream; + +} // namespace asio +} // namespace boost + +#include + +#endif // BOOST_ASIO_BUFFERED_STREAM_FWD_HPP diff --git a/include/boost/asio/buffered_write_stream.hpp b/include/boost/asio/buffered_write_stream.hpp new file mode 100644 index 00000000..654f0487 --- /dev/null +++ b/include/boost/asio/buffered_write_stream.hpp @@ -0,0 +1,366 @@ +// +// buffered_write_stream.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2005 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_BUFFERED_WRITE_STREAM_HPP +#define BOOST_ASIO_BUFFERED_WRITE_STREAM_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace boost { +namespace asio { + +/// Adds buffering to the write-related operations of a stream. +/** + * The buffered_write_stream class template can be used to add buffering to the + * synchronous and asynchronous write operations of a stream. + * + * @par Thread Safety: + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Unsafe. + * + * @par Concepts: + * Async_Object, Async_Read_Stream, Async_Write_Stream, Error_Source, Stream, + * Sync_Read_Stream, Sync_Write_Stream. + */ +template +class buffered_write_stream + : private noncopyable +{ +public: + /// The type of the next layer. + typedef typename boost::remove_reference::type next_layer_type; + + /// The type of the lowest layer. + typedef typename next_layer_type::lowest_layer_type lowest_layer_type; + + /// The demuxer type for this asynchronous type. + typedef typename next_layer_type::demuxer_type demuxer_type; + + /// The type used for reporting errors. + typedef typename next_layer_type::error_type error_type; + +#if defined(GENERATING_DOCUMENTATION) + /// The default buffer size. + static const std::size_t default_buffer_size = implementation_defined; +#else + BOOST_STATIC_CONSTANT(std::size_t, default_buffer_size = 1024); +#endif + + /// Construct, passing the specified argument to initialise the next layer. + template + explicit buffered_write_stream(Arg& a) + : next_layer_(a), + storage_(default_buffer_size) + { + } + + /// Construct, passing the specified argument to initialise the next layer. + template + buffered_write_stream(Arg& a, std::size_t buffer_size) + : next_layer_(a), + storage_(buffer_size) + { + } + + /// Get a reference to the next layer. + next_layer_type& next_layer() + { + return next_layer_; + } + + /// Get a reference to the lowest layer. + lowest_layer_type& lowest_layer() + { + return next_layer_.lowest_layer(); + } + + /// Get the demuxer associated with the asynchronous object. + demuxer_type& demuxer() + { + return next_layer_.demuxer(); + } + + /// Close the stream. + void close() + { + next_layer_.close(); + } + + /// Close the stream. + template + void close(Error_Handler error_handler) + { + next_layer_.close(error_handler); + } + + /// Flush all data from the buffer to the next layer. Returns the number of + /// bytes written to the next layer on the last write operation. Throws an + /// exception on failure. + std::size_t flush() + { + std::size_t bytes_written = write(next_layer_, + buffer(storage_.data(), storage_.size())); + storage_.consume(bytes_written); + return bytes_written; + } + + /// Flush all data from the buffer to the next layer. Returns the number of + /// bytes written to the next layer on the last write operation, or 0 if an + /// error occurred and the error handler did not throw. + template + std::size_t flush(Error_Handler error_handler) + { + std::size_t bytes_written = write(next_layer_, + buffer(storage_.data(), storage_.size()), + transfer_all(), error_handler); + storage_.consume(bytes_written); + return bytes_written; + } + + template + class flush_handler + { + public: + flush_handler(demuxer_type& demuxer, + detail::buffered_stream_storage& storage, Handler handler) + : demuxer_(demuxer), + storage_(storage), + handler_(handler) + { + } + + void operator()(const error_type& e, std::size_t bytes_written) + { + storage_.consume(bytes_written); + demuxer_.dispatch(detail::bind_handler(handler_, e, bytes_written)); + } + + private: + demuxer_type& demuxer_; + detail::buffered_stream_storage& storage_; + Handler handler_; + }; + + /// Start an asynchronous flush. + template + void async_flush(Handler handler) + { + async_write(next_layer_, buffer(storage_.data(), storage_.size()), + flush_handler(demuxer(), storage_, handler)); + } + + /// Write the given data to the stream. Returns the number of bytes written. + /// Throws an exception on failure. + template + std::size_t write_some(const Const_Buffers& buffers) + { + if (storage_.size() == storage_.capacity()) + flush(); + return copy(buffers); + } + + /// Write the given data to the stream. Returns the number of bytes written, + /// or 0 if an error occurred and the error handler did not throw. + template + std::size_t write_some(const Const_Buffers& buffers, + Error_Handler error_handler) + { + if (storage_.size() == storage_.capacity() && !flush(error_handler)) + return 0; + return copy(buffers); + } + + template + class write_some_handler + { + public: + write_some_handler(demuxer_type& demuxer, + detail::buffered_stream_storage& storage, + const Const_Buffers& buffers, Handler handler) + : demuxer_(demuxer), + storage_(storage), + buffers_(buffers), + handler_(handler) + { + } + + void operator()(const error_type& e, std::size_t bytes_written) + { + if (e) + { + std::size_t length = 0; + demuxer_.dispatch(detail::bind_handler(handler_, e, length)); + } + else + { + using namespace std; // For memcpy. + + std::size_t orig_size = storage_.size(); + std::size_t space_avail = storage_.capacity() - orig_size; + std::size_t bytes_copied = 0; + + typename Const_Buffers::const_iterator iter = buffers_.begin(); + typename Const_Buffers::const_iterator end = buffers_.end(); + for (; iter != end && space_avail > 0; ++iter) + { + std::size_t bytes_avail = buffer_size(*iter); + std::size_t length = (bytes_avail < space_avail) + ? bytes_avail : space_avail; + storage_.resize(orig_size + bytes_copied + length); + memcpy(storage_.data() + orig_size + bytes_copied, + buffer_cast(*iter), length); + bytes_copied += length; + space_avail -= length; + } + + demuxer_.dispatch(detail::bind_handler(handler_, e, bytes_copied)); + } + } + + private: + demuxer_type& demuxer_; + detail::buffered_stream_storage& storage_; + Const_Buffers buffers_; + Handler handler_; + }; + + /// Start an asynchronous write. The data being written must be valid for the + /// lifetime of the asynchronous operation. + template + void async_write_some(const Const_Buffers& buffers, Handler handler) + { + if (storage_.size() == storage_.capacity()) + { + async_flush(write_some_handler( + demuxer(), storage_, buffers, handler)); + } + else + { + std::size_t bytes_copied = copy(buffers); + demuxer().post(detail::bind_handler(handler, 0, bytes_copied)); + } + } + + /// Read some data from the stream. Returns the number of bytes read. Throws + /// an exception on failure. + template + std::size_t read_some(const Mutable_Buffers& buffers) + { + return next_layer_.read_some(buffers); + } + + /// Read some data from the stream. Returns the number of bytes read or 0 if + /// an error occurred and the error handler did not throw an exception. + template + std::size_t read_some(const Mutable_Buffers& buffers, + Error_Handler error_handler) + { + return next_layer_.read_some(buffers, error_handler); + } + + /// Start an asynchronous read. The buffer into which the data will be read + /// must be valid for the lifetime of the asynchronous operation. + template + void async_read_some(const Mutable_Buffers& buffers, Handler handler) + { + next_layer_.async_read_some(buffers, handler); + } + + /// Peek at the incoming data on the stream. Returns the number of bytes read. + /// Throws an exception on failure. + template + std::size_t peek(const Mutable_Buffers& buffers) + { + return next_layer_.peek(buffers); + } + + /// Peek at the incoming data on the stream. Returns the number of bytes read, + /// or 0 if an error occurred and the error handler did not throw. + template + std::size_t peek(const Mutable_Buffers& buffers, Error_Handler error_handler) + { + return next_layer_.peek(buffers, error_handler); + } + + /// Determine the amount of data that may be read without blocking. + std::size_t in_avail() + { + return next_layer_.in_avail(); + } + + /// Determine the amount of data that may be read without blocking. + template + std::size_t in_avail(Error_Handler error_handler) + { + return next_layer_.in_avail(error_handler); + } + +private: + /// Copy data into the internal buffer from the specified source buffer. + /// Returns the number of bytes copied. + template + std::size_t copy(const Const_Buffers& buffers) + { + using namespace std; // For memcpy. + + std::size_t orig_size = storage_.size(); + std::size_t space_avail = storage_.capacity() - orig_size; + std::size_t bytes_copied = 0; + + typename Const_Buffers::const_iterator iter = buffers.begin(); + typename Const_Buffers::const_iterator end = buffers.end(); + for (; iter != end && space_avail > 0; ++iter) + { + std::size_t bytes_avail = buffer_size(*iter); + std::size_t length = (bytes_avail < space_avail) + ? bytes_avail : space_avail; + storage_.resize(orig_size + bytes_copied + length); + memcpy(storage_.data() + orig_size + bytes_copied, + buffer_cast(*iter), length); + bytes_copied += length; + space_avail -= length; + } + + return bytes_copied; + } + + /// The next layer. + Stream next_layer_; + + // The data in the buffer. + detail::buffered_stream_storage storage_; +}; + +} // namespace asio +} // namespace boost + +#include + +#endif // BOOST_ASIO_BUFFERED_WRITE_STREAM_HPP diff --git a/include/boost/asio/buffered_write_stream_fwd.hpp b/include/boost/asio/buffered_write_stream_fwd.hpp new file mode 100644 index 00000000..80705229 --- /dev/null +++ b/include/boost/asio/buffered_write_stream_fwd.hpp @@ -0,0 +1,31 @@ +// +// buffered_write_stream_fwd.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2005 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_BUFFERED_WRITE_STREAM_FWD_HPP +#define BOOST_ASIO_BUFFERED_WRITE_STREAM_FWD_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include + +namespace boost { +namespace asio { + +template +class buffered_write_stream; + +} // namespace asio +} // namespace boost + +#include + +#endif // BOOST_ASIO_BUFFERED_WRITE_STREAM_FWD_HPP diff --git a/include/boost/asio/completion_condition.hpp b/include/boost/asio/completion_condition.hpp new file mode 100644 index 00000000..2d96b0cf --- /dev/null +++ b/include/boost/asio/completion_condition.hpp @@ -0,0 +1,103 @@ +// +// completion_condition.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2005 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_COMPLETION_CONDITION_HPP +#define BOOST_ASIO_COMPLETION_CONDITION_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include + +#include +#include +#include +#include + +namespace boost { +namespace asio { + +namespace detail { + +class transfer_all_t +{ +public: + typedef bool result_type; + + template + bool operator()(const Error& err, std::size_t bytes_transferred) + { + return !!err; + } +}; + +class transfer_at_least_t +{ +public: + typedef bool result_type; + + explicit transfer_at_least_t(std::size_t minimum) + : minimum_(minimum) + { + } + + template + bool operator()(const Error& err, std::size_t bytes_transferred) + { + return !!err || bytes_transferred >= minimum_; + } + +private: + std::size_t minimum_; +}; + +} // namespace detail + +/** + * @defgroup completion_condition Completion Condition Function Objects + * + * Function objects used for determining when a read or write operation should + * complete. + */ +/*@{*/ + +/// Return a completion condition function object that indicates that a read or +/// write operation should continue until all of the data has been transferred, +/// or until an error occurs. +#if defined(GENERATING_DOCUMENTATION) +unspecified transfer_all(); +#else +inline detail::transfer_all_t transfer_all() +{ + return detail::transfer_all_t(); +} +#endif + +/// Return a completion condition function object that indicates that a read or +/// write operation should continue until a minimum number of bytes has been +/// transferred, or until an error occurs. +#if defined(GENERATING_DOCUMENTATION) +unspecified transfer_at_least(std::size_t minimum); +#else +inline detail::transfer_at_least_t transfer_at_least(std::size_t minimum) +{ + return detail::transfer_at_least_t(minimum); +} +#endif + +/*@}*/ + +} // namespace asio +} // namespace boost + +#include + +#endif // BOOST_ASIO_COMPLETION_CONDITION_HPP diff --git a/include/boost/asio/datagram_socket.hpp b/include/boost/asio/datagram_socket.hpp new file mode 100644 index 00000000..6f39cb3b --- /dev/null +++ b/include/boost/asio/datagram_socket.hpp @@ -0,0 +1,34 @@ +// +// datagram_socket.hpp +// ~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2005 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_DATAGRAM_SOCKET_HPP +#define BOOST_ASIO_DATAGRAM_SOCKET_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include + +#include +#include + +namespace boost { +namespace asio { + +/// Typedef for the typical usage of datagram_socket. +typedef basic_datagram_socket > datagram_socket; + +} // namespace asio +} // namespace boost + +#include + +#endif // BOOST_ASIO_DATAGRAM_SOCKET_HPP diff --git a/include/boost/asio/datagram_socket_service.hpp b/include/boost/asio/datagram_socket_service.hpp new file mode 100644 index 00000000..c1a74979 --- /dev/null +++ b/include/boost/asio/datagram_socket_service.hpp @@ -0,0 +1,265 @@ +// +// datagram_socket_service.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2005 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_DATAGRAM_SOCKET_SERVICE_HPP +#define BOOST_ASIO_DATAGRAM_SOCKET_SERVICE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace boost { +namespace asio { + +/// Default service implementation for a datagram socket. +template > +class datagram_socket_service + : private noncopyable +{ +public: + /// The demuxer type. + typedef basic_demuxer > demuxer_type; + +private: + // The type of the platform-specific implementation. +#if defined(BOOST_ASIO_HAS_IOCP_DEMUXER) + typedef detail::win_iocp_socket_service service_impl_type; +#elif defined(BOOST_ASIO_HAS_EPOLL_REACTOR) + typedef detail::reactive_socket_service< + demuxer_type, detail::epoll_reactor > service_impl_type; +#elif defined(BOOST_ASIO_HAS_KQUEUE_REACTOR) + typedef detail::reactive_socket_service< + demuxer_type, detail::kqueue_reactor > service_impl_type; +#else + typedef detail::reactive_socket_service< + demuxer_type, detail::select_reactor > service_impl_type; +#endif + +public: + /// The type of a stream socket. +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined impl_type; +#else + typedef typename service_impl_type::impl_type impl_type; +#endif + + /// Construct a new stream socket service for the specified demuxer. + explicit datagram_socket_service(demuxer_type& demuxer) + : service_impl_(demuxer.get_service(service_factory())) + { + } + + /// Get the demuxer associated with the service. + demuxer_type& demuxer() + { + return service_impl_.demuxer(); + } + + /// Return a null stream socket implementation. + impl_type null() const + { + return service_impl_.null(); + } + + // Open a new datagram socket implementation. + template + void open(impl_type& impl, const Protocol& protocol, + Error_Handler error_handler) + { + if (protocol.type() == SOCK_DGRAM) + service_impl_.open(impl, protocol, error_handler); + else + error_handler(boost::asio::error(boost::asio::error::invalid_argument)); + } + + /// Assign a new datagram socket implementation. + void assign(impl_type& impl, impl_type new_impl) + { + service_impl_.assign(impl, new_impl); + } + + /// Close a stream socket implementation. + template + void close(impl_type& impl, Error_Handler error_handler) + { + service_impl_.close(impl, error_handler); + } + + // Bind the datagram socket to the specified local endpoint. + template + void bind(impl_type& impl, const Endpoint& endpoint, + Error_Handler error_handler) + { + service_impl_.bind(impl, endpoint, error_handler); + } + + /// Connect the datagram socket to the specified endpoint. + template + void connect(impl_type& impl, const Endpoint& peer_endpoint, + Error_Handler error_handler) + { + service_impl_.connect(impl, peer_endpoint, error_handler); + } + + /// Start an asynchronous connect. + template + void async_connect(impl_type& impl, const Endpoint& peer_endpoint, + Handler handler) + { + service_impl_.async_connect(impl, peer_endpoint, handler); + } + + /// Set a socket option. + template + void set_option(impl_type& impl, const Option& option, + Error_Handler error_handler) + { + service_impl_.set_option(impl, option, error_handler); + } + + /// Get a socket option. + template + void get_option(const impl_type& impl, Option& option, + Error_Handler error_handler) const + { + service_impl_.get_option(impl, option, error_handler); + } + + /// Perform an IO control command on the socket. + template + void io_control(impl_type& impl, IO_Control_Command& command, + Error_Handler error_handler) + { + service_impl_.io_control(impl, command, error_handler); + } + + /// Get the local endpoint. + template + void get_local_endpoint(const impl_type& impl, Endpoint& endpoint, + Error_Handler error_handler) const + { + service_impl_.get_local_endpoint(impl, endpoint, error_handler); + } + + // Get the remote endpoint. + template + void get_remote_endpoint(const impl_type& impl, Endpoint& endpoint, + Error_Handler error_handler) const + { + service_impl_.get_remote_endpoint(impl, endpoint, error_handler); + } + + /// Disable sends or receives on the socket. + template + void shutdown(impl_type& impl, socket_base::shutdown_type what, + Error_Handler error_handler) + { + service_impl_.shutdown(impl, what, error_handler); + } + + /// Send the given data to the peer. + template + std::size_t send(impl_type& impl, const Const_Buffers& buffers, + socket_base::message_flags flags, Error_Handler error_handler) + { + return service_impl_.send(impl, buffers, flags, error_handler); + } + + /// Start an asynchronous send. + template + void async_send(impl_type& impl, const Const_Buffers& buffers, + socket_base::message_flags flags, Handler handler) + { + service_impl_.async_send(impl, buffers, flags, handler); + } + + /// Send a datagram to the specified endpoint. + template + std::size_t send_to(impl_type& impl, const Const_Buffers& buffers, + socket_base::message_flags flags, const Endpoint& destination, + Error_Handler error_handler) + { + return service_impl_.send_to(impl, buffers, flags, destination, + error_handler); + } + + /// Start an asynchronous send. + template + void async_send_to(impl_type& impl, const Const_Buffers& buffers, + socket_base::message_flags flags, const Endpoint& destination, + Handler handler) + { + service_impl_.async_send_to(impl, buffers, flags, destination, handler); + } + + /// Receive some data from the peer. + template + std::size_t receive(impl_type& impl, const Mutable_Buffers& buffers, + socket_base::message_flags flags, Error_Handler error_handler) + { + return service_impl_.receive(impl, buffers, flags, error_handler); + } + + /// Start an asynchronous receive. + template + void async_receive(impl_type& impl, const Mutable_Buffers& buffers, + socket_base::message_flags flags, Handler handler) + { + service_impl_.async_receive(impl, buffers, flags, handler); + } + + /// Receive a datagram with the endpoint of the sender. + template + std::size_t receive_from(impl_type& impl, const Mutable_Buffers& buffers, + socket_base::message_flags flags, Endpoint& sender_endpoint, + Error_Handler error_handler) + { + return service_impl_.receive_from(impl, buffers, flags, sender_endpoint, + error_handler); + } + + /// Start an asynchronous receive that will get the endpoint of the sender. + template + void async_receive_from(impl_type& impl, const Mutable_Buffers& buffers, + socket_base::message_flags flags, Endpoint& sender_endpoint, + Handler handler) + { + service_impl_.async_receive_from(impl, buffers, flags, sender_endpoint, + handler); + } + +private: + // The service that provides the platform-specific implementation. + service_impl_type& service_impl_; +}; + +} // namespace asio +} // namespace boost + +#include + +#endif // BOOST_ASIO_DATAGRAM_SOCKET_SERVICE_HPP diff --git a/include/boost/asio/deadline_timer.hpp b/include/boost/asio/deadline_timer.hpp new file mode 100644 index 00000000..892748e4 --- /dev/null +++ b/include/boost/asio/deadline_timer.hpp @@ -0,0 +1,34 @@ +// +// deadline_timer.hpp +// ~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2005 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_DEADLINE_TIMER_HPP +#define BOOST_ASIO_DEADLINE_TIMER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include + +#include +#include + +namespace boost { +namespace asio { + +/// Typedef for the typical usage of timer. +typedef basic_deadline_timer > deadline_timer; + +} // namespace asio +} // namespace boost + +#include + +#endif // BOOST_ASIO_DEADLINE_TIMER_HPP diff --git a/include/boost/asio/deadline_timer_service.hpp b/include/boost/asio/deadline_timer_service.hpp new file mode 100644 index 00000000..f877b0d4 --- /dev/null +++ b/include/boost/asio/deadline_timer_service.hpp @@ -0,0 +1,169 @@ +// +// deadline_timer_service.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2005 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_DEADLINE_TIMER_SERVICE_HPP +#define BOOST_ASIO_DEADLINE_TIMER_SERVICE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include + +#include // Must come before posix_time. + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace boost { +namespace asio { + +/// Default service implementation for a timer. +template , + typename Allocator = std::allocator > +class deadline_timer_service + : private noncopyable +{ +public: + /// The demuxer type. + typedef basic_demuxer > demuxer_type; + + /// The time traits type. + typedef Time_Traits traits_type; + + /// The time type. + typedef typename traits_type::time_type time_type; + + /// The duration type. + typedef typename traits_type::duration_type duration_type; + +private: + // The type of the platform-specific implementation. +#if defined(BOOST_ASIO_HAS_IOCP_DEMUXER) + typedef detail::reactive_deadline_timer_service > service_impl_type; +#elif defined(BOOST_ASIO_HAS_EPOLL_REACTOR) + typedef detail::reactive_deadline_timer_service > service_impl_type; +#elif defined(BOOST_ASIO_HAS_KQUEUE_REACTOR) + typedef detail::reactive_deadline_timer_service > service_impl_type; +#else + typedef detail::reactive_deadline_timer_service > service_impl_type; +#endif + +public: + /// The native type of the deadline timer. +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined impl_type; +#else + typedef typename service_impl_type::impl_type impl_type; +#endif + + /// Construct a new timer service for the specified demuxer. + explicit deadline_timer_service(demuxer_type& demuxer) + : service_impl_(demuxer.get_service(service_factory())) + { + } + + /// Get the demuxer associated with the service. + demuxer_type& demuxer() + { + return service_impl_.demuxer(); + } + + /// Return a null timer implementation. + impl_type null() const + { + return service_impl_.null(); + } + + /// Create a new timer implementation. + void create(impl_type& impl) + { + service_impl_.create(impl); + } + + /// Destroy a timer implementation. + void destroy(impl_type& impl) + { + service_impl_.destroy(impl); + } + + /// Get the expiry time for the timer as an absolute time. + time_type expires_at(const impl_type& impl) const + { + return service_impl_.expires_at(impl); + } + + /// Set the expiry time for the timer as an absolute time. + void expires_at(impl_type& impl, const time_type& expiry_time) + { + service_impl_.expires_at(impl, expiry_time); + } + + /// Get the expiry time for the timer relative to now. + duration_type expires_from_now(const impl_type& impl) const + { + return service_impl_.expires_from_now(impl); + } + + /// Set the expiry time for the timer relative to now. + void expires_from_now(impl_type& impl, const duration_type& expiry_time) + { + service_impl_.expires_from_now(impl, expiry_time); + } + + /// Cancel any asynchronous wait operations associated with the timer. + std::size_t cancel(impl_type& impl) + { + return service_impl_.cancel(impl); + } + + // Perform a blocking wait on the timer. + void wait(impl_type& impl) + { + service_impl_.wait(impl); + } + + // Start an asynchronous wait on the timer. + template + void async_wait(impl_type& impl, Handler handler) + { + service_impl_.async_wait(impl, handler); + } + +private: + // The service that provides the platform-specific implementation. + service_impl_type& service_impl_; +}; + +} // namespace asio +} // namespace boost + +#include + +#endif // BOOST_ASIO_DEADLINE_TIMER_SERVICE_HPP diff --git a/include/boost/asio/demuxer.hpp b/include/boost/asio/demuxer.hpp new file mode 100644 index 00000000..6db63ab5 --- /dev/null +++ b/include/boost/asio/demuxer.hpp @@ -0,0 +1,34 @@ +// +// demuxer.hpp +// ~~~~~~~~~~~ +// +// Copyright (c) 2003-2005 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_DEMUXER_HPP +#define BOOST_ASIO_DEMUXER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include + +#include +#include + +namespace boost { +namespace asio { + +/// Typedef for typical usage of demuxer. +typedef basic_demuxer > demuxer; + +} // namespace asio +} // namespace boost + +#include + +#endif // BOOST_ASIO_DEMUXER_HPP diff --git a/include/boost/asio/demuxer_service.hpp b/include/boost/asio/demuxer_service.hpp new file mode 100644 index 00000000..3cd95158 --- /dev/null +++ b/include/boost/asio/demuxer_service.hpp @@ -0,0 +1,172 @@ +// +// demuxer_service.hpp +// ~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2005 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_DEMUXER_SERVICE_HPP +#define BOOST_ASIO_DEMUXER_SERVICE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace boost { +namespace asio { + +/// Default service implementation for a demuxer. +template > +class demuxer_service + : private noncopyable +{ +public: + /// The demuxer type for this service. + typedef basic_demuxer > demuxer_type; + + /// The allocator type for this service. + typedef Allocator allocator_type; + +private: + // The type of the platform-specific implementation. +#if defined(BOOST_ASIO_HAS_IOCP_DEMUXER) + typedef detail::win_iocp_demuxer_service service_impl_type; +#elif defined(BOOST_ASIO_HAS_EPOLL_REACTOR) + typedef detail::task_demuxer_service > + service_impl_type; +#elif defined(BOOST_ASIO_HAS_KQUEUE_REACTOR) + typedef detail::task_demuxer_service > + service_impl_type; +#else + typedef detail::task_demuxer_service > + service_impl_type; +#endif + +public: + /// Constructor. + explicit demuxer_service(demuxer_type& demuxer) + : service_impl_(demuxer.get_service(service_factory())) + { + } + + /// Constructor. + demuxer_service(demuxer_type& demuxer, const allocator_type& allocator) + : service_impl_(demuxer.get_service(service_factory())), + allocator_(allocator) + { + } + + /// Return a copy of the allocator associated with the service. + allocator_type get_allocator() const + { + return allocator_; + } + + /// Run the demuxer's event processing loop. + void run() + { + service_impl_.run(); + } + + /// Interrupt the demuxer's event processing loop. + void interrupt() + { + service_impl_.interrupt(); + } + + /// Reset the demuxer in preparation for a subsequent run invocation. + void reset() + { + service_impl_.reset(); + } + + /// Notify the demuxer that some work has started. + void work_started() + { + service_impl_.work_started(); + } + + /// Notify the demuxer that some work has finished. + void work_finished() + { + service_impl_.work_finished(); + } + + /// Request the demuxer to invoke the given handler. + template + void dispatch(Handler handler) + { + service_impl_.dispatch(handler); + } + + /// Request the demuxer to invoke the given handler and return immediately. + template + void post(Handler handler) + { + service_impl_.post(handler); + } + +private: + // The service that provides the platform-specific implementation. + service_impl_type& service_impl_; + + // The allocator associated with the service. + allocator_type allocator_; +}; + +#if !defined(BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION) + +/// Specialisation of service_factory to allow an allocator to be specified. +template +class service_factory > +{ +public: + /// Default constructor. + service_factory() + { + } + + /// Construct with a specified allocator. + explicit service_factory(const Allocator& allocator) + : allocator_(allocator) + { + } + + /// Create a service with the specified owner. + template + demuxer_service* create(Owner& owner) + { + return new demuxer_service(owner, allocator_); + } + +private: + // The allocator to be passed to the service. + Allocator allocator_; +}; + +#endif // !defined(BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION) + +} // namespace asio +} // namespace boost + +#include + +#endif // BOOST_ASIO_DEMUXER_SERVICE_HPP diff --git a/include/boost/asio/detail/bind_handler.hpp b/include/boost/asio/detail/bind_handler.hpp new file mode 100644 index 00000000..66cee89a --- /dev/null +++ b/include/boost/asio/detail/bind_handler.hpp @@ -0,0 +1,223 @@ +// +// bind.hpp +// ~~~~~~~~ +// +// Copyright (c) 2003-2005 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_DETAIL_BIND_HPP +#define BOOST_ASIO_DETAIL_BIND_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include + +namespace boost { +namespace asio { +namespace detail { + +// Some compilers (notably MSVC6) run into mysterious compiler errors when +// trying to use the boost::bind template in this library. The class and +// function templates below provide only the functionality of bind to create +// function objects with the signature void() as used in handlers passed to a +// demuxer's dispatch or post functions. This should make it simpler for the +// compiler to work correctly. + +template +class binder1 +{ +public: + binder1(Handler handler, Arg1 arg1) + : handler_(handler), + arg1_(arg1) + { + } + + void operator()() + { + handler_(arg1_); + } + + void operator()() const + { + handler_(arg1_); + } + +private: + Handler handler_; + Arg1 arg1_; +}; + +template +binder1 bind_handler(Handler handler, Arg1 arg1) +{ + return binder1(handler, arg1); +} + +template +class binder2 +{ +public: + binder2(Handler handler, Arg1 arg1, Arg2 arg2) + : handler_(handler), + arg1_(arg1), + arg2_(arg2) + { + } + + void operator()() + { + handler_(arg1_, arg2_); + } + + void operator()() const + { + handler_(arg1_, arg2_); + } + +private: + Handler handler_; + Arg1 arg1_; + Arg2 arg2_; +}; + +template +binder2 bind_handler(Handler handler, Arg1 arg1, + Arg2 arg2) +{ + return binder2(handler, arg1, arg2); +} + +template +class binder3 +{ +public: + binder3(Handler handler, Arg1 arg1, Arg2 arg2, Arg3 arg3) + : handler_(handler), + arg1_(arg1), + arg2_(arg2), + arg3_(arg3) + { + } + + void operator()() + { + handler_(arg1_, arg2_, arg3_); + } + + void operator()() const + { + handler_(arg1_, arg2_, arg3_); + } + +private: + Handler handler_; + Arg1 arg1_; + Arg2 arg2_; + Arg3 arg3_; +}; + +template +binder3 bind_handler(Handler handler, Arg1 arg1, + Arg2 arg2, Arg3 arg3) +{ + return binder3(handler, arg1, arg2, arg3); +} + +template +class binder4 +{ +public: + binder4(Handler handler, Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4) + : handler_(handler), + arg1_(arg1), + arg2_(arg2), + arg3_(arg3), + arg4_(arg4) + { + } + + void operator()() + { + handler_(arg1_, arg2_, arg3_, arg4_); + } + + void operator()() const + { + handler_(arg1_, arg2_, arg3_, arg4_); + } + +private: + Handler handler_; + Arg1 arg1_; + Arg2 arg2_; + Arg3 arg3_; + Arg4 arg4_; +}; + +template +binder4 bind_handler(Handler handler, + Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4) +{ + return binder4(handler, arg1, arg2, arg3, + arg4); +} + +template +class binder5 +{ +public: + binder5(Handler handler, Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4, + Arg5 arg5) + : handler_(handler), + arg1_(arg1), + arg2_(arg2), + arg3_(arg3), + arg4_(arg4), + arg5_(arg5) + { + } + + void operator()() + { + handler_(arg1_, arg2_, arg3_, arg4_, arg5_); + } + + void operator()() const + { + handler_(arg1_, arg2_, arg3_, arg4_, arg5_); + } + +private: + Handler handler_; + Arg1 arg1_; + Arg2 arg2_; + Arg3 arg3_; + Arg4 arg4_; + Arg5 arg5_; +}; + +template +binder5 bind_handler(Handler handler, + Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4, Arg5 arg5) +{ + return binder5(handler, arg1, arg2, + arg3, arg4, arg5); +} + +} // namespace detail +} // namespace asio +} // namespace boost + +#include + +#endif // BOOST_ASIO_DETAIL_BIND_HPP diff --git a/include/boost/asio/detail/buffer_resize_guard.hpp b/include/boost/asio/detail/buffer_resize_guard.hpp new file mode 100644 index 00000000..1961e238 --- /dev/null +++ b/include/boost/asio/detail/buffer_resize_guard.hpp @@ -0,0 +1,72 @@ +// +// buffer_resize_guard.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2005 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_DETAIL_BUFFER_RESIZE_GUARD_HPP +#define BOOST_ASIO_DETAIL_BUFFER_RESIZE_GUARD_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include + +#include +#include +#include +#include + +namespace boost { +namespace asio { +namespace detail { + +// Helper class to manage buffer resizing in an exception safe way. +template +class buffer_resize_guard +{ +public: + // Constructor. + buffer_resize_guard(Buffer& buffer) + : buffer_(buffer), + old_size_(buffer.size()) + { + } + + // Destructor rolls back the buffer resize unless commit was called. + ~buffer_resize_guard() + { + if (old_size_ + != std::numeric_limits::max BOOST_PREVENT_MACRO_SUBSTITUTION()) + { + buffer_.resize(old_size_); + } + } + + // Commit the resize transaction. + void commit() + { + old_size_ + = std::numeric_limits::max BOOST_PREVENT_MACRO_SUBSTITUTION(); + } + +private: + // The buffer being managed. + Buffer& buffer_; + + // The size of the buffer at the time the guard was constructed. + size_t old_size_; +}; + +} // namespace detail +} // namespace asio +} // namespace boost + +#include + +#endif // BOOST_ASIO_DETAIL_BUFFER_RESIZE_GUARD_HPP diff --git a/include/boost/asio/detail/buffered_stream_storage.hpp b/include/boost/asio/detail/buffered_stream_storage.hpp new file mode 100644 index 00000000..673ac4af --- /dev/null +++ b/include/boost/asio/detail/buffered_stream_storage.hpp @@ -0,0 +1,129 @@ +// +// buffered_stream_storage.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2005 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_DETAIL_BUFFERED_STREAM_STORAGE_HPP +#define BOOST_ASIO_DETAIL_BUFFERED_STREAM_STORAGE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace boost { +namespace asio { +namespace detail { + +class buffered_stream_storage +{ +public: + // The type of the bytes stored in the buffer. + typedef unsigned char byte_type; + + // The type used for offsets into the buffer. + typedef std::size_t size_type; + + // Constructor. + explicit buffered_stream_storage(std::size_t capacity) + : begin_offset_(0), + end_offset_(0), + buffer_(capacity) + { + } + + /// Clear the buffer. + void clear() + { + begin_offset_ = 0; + end_offset_ = 0; + } + + // Return a pointer to the beginning of the unread data. + byte_type* data() + { + return &buffer_[0] + begin_offset_; + } + + // Return a pointer to the beginning of the unread data. + const byte_type* data() const + { + return &buffer_[0] + begin_offset_; + } + + // Is there no unread data in the buffer. + bool empty() const + { + return begin_offset_ == end_offset_; + } + + // Return the amount of unread data the is in the buffer. + size_type size() const + { + return end_offset_ - begin_offset_; + } + + // Resize the buffer to the specified length. + void resize(size_type length) + { + assert(length <= capacity()); + if (begin_offset_ + length <= capacity()) + { + end_offset_ = begin_offset_ + length; + } + else + { + using namespace std; // For memmove. + memmove(&buffer_[0], &buffer_[0] + begin_offset_, size()); + end_offset_ = length; + begin_offset_ = 0; + } + } + + // Return the maximum size for data in the buffer. + size_type capacity() const + { + return buffer_.size(); + } + + // Consume multiple bytes from the beginning of the buffer. + void consume(size_type count) + { + assert(begin_offset_ + count <= end_offset_); + begin_offset_ += count; + if (empty()) + clear(); + } + +private: + // The offset to the beginning of the unread data. + size_type begin_offset_; + + // The offset to the end of the unread data. + size_type end_offset_; + + // The data in the buffer. + std::vector buffer_; +}; + +} // namespace detail +} // namespace asio +} // namespace boost + +#include + +#endif // BOOST_ASIO_DETAIL_BUFFERED_STREAM_STORAGE_HPP diff --git a/include/boost/asio/detail/consuming_buffers.hpp b/include/boost/asio/detail/consuming_buffers.hpp new file mode 100644 index 00000000..7f40e595 --- /dev/null +++ b/include/boost/asio/detail/consuming_buffers.hpp @@ -0,0 +1,133 @@ +// +// consuming_buffers.hpp +// ~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2005 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_DETAIL_CONSUMING_BUFFERS_HPP +#define BOOST_ASIO_DETAIL_CONSUMING_BUFFERS_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include + +#include +#include +#include +#include +#include + +#include + +namespace boost { +namespace asio { +namespace detail { + +// A proxy for a sub-range in a list of buffers. +template +class consuming_buffers +{ +public: + // The type for each element in the list of buffers. + typedef typename Buffers::value_type value_type; + + // A forward-only iterator type that may be used to read or modify elements. + typedef typename Buffers::iterator iterator; + + // A forward-only iterator type that may be used to read elements. + typedef typename Buffers::const_iterator const_iterator; + + // Construct to represent the entire list of buffers. + consuming_buffers(const Buffers& buffers) + : buffers_(buffers), + begin_(buffers_.begin()) + { + } + + // Copy constructor. + consuming_buffers(const consuming_buffers& other) + : buffers_(other.buffers_), + begin_(buffers_.begin()) + { + const_iterator first = other.buffers_.begin(); + const_iterator second = other.begin_; + std::advance(begin_, std::distance(first, second)); + } + + // Assignment operator. + consuming_buffers& operator=(const consuming_buffers& other) + { + buffers_ = other.buffers_; + begin_ = buffers_.begin(); + const_iterator first = other.buffers_.begin(); + const_iterator second = other.begin_; + std::advance(begin_, std::distance(first, second)); + return *this; + } + + // Get a forward-only iterator to the first element. + iterator begin() + { + return begin_; + } + + // Get a forward-only iterator to the first element. + const_iterator begin() const + { + return begin_; + } + + // Get a forward-only iterator for one past the last element. + iterator end() + { + return buffers_.end(); + } + + // Get a forward-only iterator for one past the last element. + const_iterator end() const + { + return buffers_.end(); + } + + // Consume the specified number of bytes from the buffers. + void consume(std::size_t size) + { + // Remove buffers from the start until the specified size is reached. + while (size > 0 && begin_ != buffers_.end()) + { + if (buffer_size(*begin_) <= size) + { + size -= buffer_size(*begin_); + *begin_ = value_type(); + ++begin_; + } + else + { + *begin_ = *begin_ + size; + size = 0; + } + } + + // Remove any more empty buffers at the start. + while (begin_ != buffers_.end() && buffer_size(*begin_) == 0) + ++begin_; + } + +private: + Buffers buffers_; + iterator begin_; +}; + +} // namespace detail +} // namespace asio +} // namespace boost + +#include + +#endif // BOOST_ASIO_DETAIL_CONSUMING_BUFFERS_HPP diff --git a/include/boost/asio/detail/demuxer_run_call_stack.hpp b/include/boost/asio/detail/demuxer_run_call_stack.hpp new file mode 100644 index 00000000..244d8c00 --- /dev/null +++ b/include/boost/asio/detail/demuxer_run_call_stack.hpp @@ -0,0 +1,92 @@ +// +// demuxer_run_call_stack.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2005 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_DETAIL_DEMUXER_RUN_CALL_STACK_HPP +#define BOOST_ASIO_DETAIL_DEMUXER_RUN_CALL_STACK_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include + +#include +#include + +namespace boost { +namespace asio { +namespace detail { + +// Helper class to determine whether or not the current thread is inside an +// invocation of demuxer::run() for a specified demuxer. +template +class demuxer_run_call_stack +{ +public: + // Context class automatically pushes a demuxer on to the stack. + class context + : private noncopyable + { + public: + // Push the demuxer on to the stack. + explicit context(Demuxer_Service* d) + : demuxer_service_(d), + next_(demuxer_run_call_stack::top_) + { + demuxer_run_call_stack::top_ = this; + } + + // Pop the demuxer from the stack. + ~context() + { + demuxer_run_call_stack::top_ = next_; + } + + private: + friend class demuxer_run_call_stack; + + // The demuxer service associated with the context. + Demuxer_Service* demuxer_service_; + + // The next element in the stack. + context* next_; + }; + + friend class context; + + // Determine whether the specified demuxer is on the stack. + static bool contains(Demuxer_Service* d) + { + context* elem = top_; + while (elem) + { + if (elem->demuxer_service_ == d) + return true; + elem = elem->next_; + } + return false; + } + +private: + // The top of the stack of demuxer::run() calls for the current thread. + static tss_ptr top_; +}; + +template +tss_ptr::context> +demuxer_run_call_stack::top_; + +} // namespace detail +} // namespace asio +} // namespace boost + +#include + +#endif // BOOST_ASIO_DETAIL_DEMUXER_RUN_CALL_STACK_HPP diff --git a/include/boost/asio/detail/epoll_reactor.hpp b/include/boost/asio/detail/epoll_reactor.hpp new file mode 100644 index 00000000..0503e4d1 --- /dev/null +++ b/include/boost/asio/detail/epoll_reactor.hpp @@ -0,0 +1,567 @@ +// +// epoll_reactor.hpp +// ~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2005 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_DETAIL_EPOLL_REACTOR_HPP +#define BOOST_ASIO_DETAIL_EPOLL_REACTOR_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include + +#if defined(__linux__) // This service is only supported on Linux. + +#include +#include +#include + +#if LINUX_VERSION_CODE >= KERNEL_VERSION (2,5,45) // Only kernels >= 2.5.45. + +// Define this to indicate that epoll is supported on the target platform. +#define BOOST_ASIO_HAS_EPOLL_REACTOR 1 + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace boost { +namespace asio { +namespace detail { + +template +class epoll_reactor + : private noncopyable +{ +public: + // Constructor. + template + epoll_reactor(Demuxer&) + : mutex_(), + epoll_fd_(do_epoll_create()), + wait_in_progress_(false), + interrupter_(), + read_op_queue_(), + write_op_queue_(), + except_op_queue_(), + epoll_registrations_(), + pending_cancellations_(), + stop_thread_(false), + thread_(0) + { + // Start the reactor's internal thread only if needed. + if (Own_Thread) + { + boost::asio::detail::signal_blocker sb; + thread_ = new boost::asio::detail::thread( + bind_handler(&epoll_reactor::call_run_thread, this)); + } + + // Add the interrupter's descriptor to epoll. + epoll_event ev = { 0 }; + ev.events = EPOLLIN | EPOLLERR; + ev.data.fd = interrupter_.read_descriptor(); + epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, interrupter_.read_descriptor(), &ev); + } + + // Destructor. + ~epoll_reactor() + { + if (thread_) + { + boost::asio::detail::mutex::scoped_lock lock(mutex_); + stop_thread_ = true; + lock.unlock(); + interrupter_.interrupt(); + thread_->join(); + delete thread_; + } + + close(epoll_fd_); + } + + // Start a new read operation. The handler object will be invoked when the + // given descriptor is ready to be read, or an error has occurred. + template + void start_read_op(socket_type descriptor, Handler handler) + { + boost::asio::detail::mutex::scoped_lock lock(mutex_); + + if (read_op_queue_.enqueue_operation(descriptor, handler)) + { + epoll_event ev = { 0 }; + ev.events = EPOLLIN | EPOLLERR | EPOLLHUP; + if (write_op_queue_.has_operation(descriptor)) + ev.events |= EPOLLOUT; + if (except_op_queue_.has_operation(descriptor)) + ev.events |= EPOLLPRI; + ev.data.fd = descriptor; + + int result; + if (epoll_registrations_.find(descriptor) == epoll_registrations_.end()) + { + epoll_registrations_.insert( + epoll_registration_map::value_type(descriptor, true)); + result = epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, descriptor, &ev); + } + else + { + result = epoll_ctl(epoll_fd_, EPOLL_CTL_MOD, descriptor, &ev); + } + + if (result != 0) + { + int error = errno; + read_op_queue_.dispatch_all_operations(descriptor, error); + } + } + } + + // Start a new write operation. The handler object will be invoked when the + // given descriptor is ready to be written, or an error has occurred. + template + void start_write_op(socket_type descriptor, Handler handler) + { + boost::asio::detail::mutex::scoped_lock lock(mutex_); + + if (write_op_queue_.enqueue_operation(descriptor, handler)) + { + epoll_event ev = { 0 }; + ev.events = EPOLLOUT | EPOLLERR | EPOLLHUP; + if (read_op_queue_.has_operation(descriptor)) + ev.events |= EPOLLIN; + if (except_op_queue_.has_operation(descriptor)) + ev.events |= EPOLLPRI; + ev.data.fd = descriptor; + + int result; + if (epoll_registrations_.find(descriptor) == epoll_registrations_.end()) + { + epoll_registrations_.insert( + epoll_registration_map::value_type(descriptor, true)); + result = epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, descriptor, &ev); + } + else + { + result = epoll_ctl(epoll_fd_, EPOLL_CTL_MOD, descriptor, &ev); + } + + if (result != 0) + { + int error = errno; + write_op_queue_.dispatch_all_operations(descriptor, error); + } + } + } + + // Start a new exception operation. The handler object will be invoked when + // the given descriptor has exception information, or an error has occurred. + template + void start_except_op(socket_type descriptor, Handler handler) + { + boost::asio::detail::mutex::scoped_lock lock(mutex_); + + if (except_op_queue_.enqueue_operation(descriptor, handler)) + { + epoll_event ev = { 0 }; + ev.events = EPOLLPRI | EPOLLERR | EPOLLHUP; + if (read_op_queue_.has_operation(descriptor)) + ev.events |= EPOLLIN; + if (write_op_queue_.has_operation(descriptor)) + ev.events |= EPOLLOUT; + ev.data.fd = descriptor; + + int result; + if (epoll_registrations_.find(descriptor) == epoll_registrations_.end()) + { + epoll_registrations_.insert( + epoll_registration_map::value_type(descriptor, true)); + result = epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, descriptor, &ev); + } + else + { + result = epoll_ctl(epoll_fd_, EPOLL_CTL_MOD, descriptor, &ev); + } + + if (result != 0) + { + int error = errno; + except_op_queue_.dispatch_all_operations(descriptor, error); + } + } + } + + // Start new write and exception operations. The handler object will be + // invoked when the given descriptor is ready for writing or has exception + // information available, or an error has occurred. + template + void start_write_and_except_ops(socket_type descriptor, Handler handler) + { + boost::asio::detail::mutex::scoped_lock lock(mutex_); + + bool need_mod = write_op_queue_.enqueue_operation(descriptor, handler); + need_mod = except_op_queue_.enqueue_operation(descriptor, handler) + && need_mod; + if (need_mod) + { + epoll_event ev = { 0 }; + ev.events = EPOLLOUT | EPOLLPRI | EPOLLERR | EPOLLHUP; + if (read_op_queue_.has_operation(descriptor)) + ev.events |= EPOLLIN; + ev.data.fd = descriptor; + + int result; + if (epoll_registrations_.find(descriptor) == epoll_registrations_.end()) + { + epoll_registrations_.insert( + epoll_registration_map::value_type(descriptor, true)); + result = epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, descriptor, &ev); + } + else + { + result = epoll_ctl(epoll_fd_, EPOLL_CTL_MOD, descriptor, &ev); + } + + if (result != 0) + { + int error = errno; + write_op_queue_.dispatch_all_operations(descriptor, error); + except_op_queue_.dispatch_all_operations(descriptor, error); + } + } + } + + // Cancel all operations associated with the given descriptor. The + // handlers associated with the descriptor will be invoked with the + // operation_aborted error. + void cancel_ops(socket_type descriptor) + { + boost::asio::detail::mutex::scoped_lock lock(mutex_); + cancel_ops_unlocked(descriptor); + } + + // Enqueue cancellation of all operations associated with the given + // descriptor. The handlers associated with the descriptor will be invoked + // with the operation_aborted error. This function does not acquire the + // select_reactor's mutex, and so should only be used from within a reactor + // handler. + void enqueue_cancel_ops_unlocked(socket_type descriptor) + { + pending_cancellations_.insert( + pending_cancellations_map::value_type(descriptor, true)); + } + + // Cancel any operations that are running against the descriptor and remove + // its registration from the reactor. + void close_descriptor(socket_type descriptor) + { + boost::asio::detail::mutex::scoped_lock lock(mutex_); + + // Remove the descriptor from epoll. + epoll_registration_map::iterator it = epoll_registrations_.find(descriptor); + if (it != epoll_registrations_.end()) + { + epoll_event ev = { 0 }; + epoll_ctl(epoll_fd_, EPOLL_CTL_DEL, descriptor, &ev); + epoll_registrations_.erase(it); + } + + // Cancel any outstanding operations associated with the descriptor. + cancel_ops_unlocked(descriptor); + } + + // Schedule a timer to expire at the specified absolute time. The handler + // object will be invoked when the timer expires. + template + void schedule_timer(const boost::posix_time::ptime& time, + Handler handler, void* token) + { + boost::asio::detail::mutex::scoped_lock lock(mutex_); + if (timer_queue_.enqueue_timer(time, handler, token)) + interrupter_.interrupt(); + } + + // Cancel the timer associated with the given token. Returns the number of + // handlers that have been posted or dispatched. + std::size_t cancel_timer(void* token) + { + boost::asio::detail::mutex::scoped_lock lock(mutex_); + return timer_queue_.cancel_timer(token); + } + +private: + friend class task_demuxer_service >; + + // Reset the select loop before a new run. + void reset() + { + boost::asio::detail::mutex::scoped_lock lock(mutex_); + stop_thread_ = false; + interrupter_.reset(); + } + + // Run the epoll loop. + void run() + { + boost::asio::detail::mutex::scoped_lock lock(mutex_); + + // Dispatch any operation cancellations that were made while the select + // loop was not running. + read_op_queue_.dispatch_cancellations(); + write_op_queue_.dispatch_cancellations(); + except_op_queue_.dispatch_cancellations(); + + bool stop = false; + while (!stop && !stop_thread_) + { + int timeout = get_timeout(); + wait_in_progress_ = true; + lock.unlock(); + + // Block on the epoll descriptor. + epoll_event events[128]; + int num_events = epoll_wait(epoll_fd_, events, 128, timeout); + + lock.lock(); + wait_in_progress_ = false; + + // Block signals while dispatching operations. + boost::asio::detail::signal_blocker sb; + + // Dispatch the waiting events. + for (int i = 0; i < num_events; ++i) + { + int descriptor = events[i].data.fd; + if (descriptor == interrupter_.read_descriptor()) + { + stop = interrupter_.reset(); + } + else + { + if (events[i].events & (EPOLLERR | EPOLLHUP)) + { + except_op_queue_.dispatch_all_operations(descriptor, 0); + read_op_queue_.dispatch_all_operations(descriptor, 0); + write_op_queue_.dispatch_all_operations(descriptor, 0); + + epoll_event ev = { 0 }; + ev.events = 0; + ev.data.fd = descriptor; + epoll_ctl(epoll_fd_, EPOLL_CTL_MOD, descriptor, &ev); + } + else + { + bool more_reads = false; + bool more_writes = false; + bool more_except = false; + + // Exception operations must be processed first to ensure that any + // out-of-band data is read before normal data. + if (events[i].events & EPOLLPRI) + more_except = except_op_queue_.dispatch_operation(descriptor, 0); + else + more_except = except_op_queue_.has_operation(descriptor); + + if (events[i].events & EPOLLIN) + more_reads = read_op_queue_.dispatch_operation(descriptor, 0); + else + more_reads = read_op_queue_.has_operation(descriptor); + + if (events[i].events & EPOLLOUT) + more_writes = write_op_queue_.dispatch_operation(descriptor, 0); + else + more_writes = write_op_queue_.has_operation(descriptor); + + epoll_event ev = { 0 }; + ev.events = EPOLLERR | EPOLLHUP; + if (more_reads) + ev.events |= EPOLLIN; + if (more_writes) + ev.events |= EPOLLOUT; + if (more_except) + ev.events |= EPOLLPRI; + ev.data.fd = descriptor; + int result = epoll_ctl(epoll_fd_, EPOLL_CTL_MOD, descriptor, &ev); + if (result != 0) + { + int error = errno; + read_op_queue_.dispatch_all_operations(descriptor, error); + write_op_queue_.dispatch_all_operations(descriptor, error); + except_op_queue_.dispatch_all_operations(descriptor, error); + } + } + } + } + read_op_queue_.dispatch_cancellations(); + write_op_queue_.dispatch_cancellations(); + except_op_queue_.dispatch_cancellations(); + timer_queue_.dispatch_timers( + boost::posix_time::microsec_clock::universal_time()); + + // Issue any pending cancellations. + pending_cancellations_map::iterator i = pending_cancellations_.begin(); + while (i != pending_cancellations_.end()) + { + cancel_ops_unlocked(i->first); + ++i; + } + pending_cancellations_.clear(); + } + } + + // Run the select loop in the thread. + void run_thread() + { + boost::asio::detail::mutex::scoped_lock lock(mutex_); + while (!stop_thread_) + { + lock.unlock(); + run(); + lock.lock(); + } + } + + // Entry point for the select loop thread. + static void call_run_thread(epoll_reactor* reactor) + { + reactor->run_thread(); + } + + // Interrupt the select loop. + void interrupt() + { + interrupter_.interrupt(); + } + + // The hint to pass to epoll_create to size its data structures. + enum { epoll_size = 20000 }; + + // Create the epoll file descriptor. Throws an exception if the descriptor + // cannot be created. + static int do_epoll_create() + { + int fd = epoll_create(epoll_size); + if (fd == -1) + { + system_exception e("epoll", errno); + boost::throw_exception(e); + } + return fd; + } + + // Get the timeout value for the epoll_wait call. The timeout value is + // returned as a number of milliseconds. A return value of -1 indicates + // that epoll_wait should block indefinitely. + int get_timeout() + { + if (timer_queue_.empty()) + return -1; + + boost::posix_time::ptime now + = boost::posix_time::microsec_clock::universal_time(); + boost::posix_time::ptime earliest_timer; + timer_queue_.get_earliest_time(earliest_timer); + if (now < earliest_timer) + { + boost::posix_time::time_duration timeout = earliest_timer - now; + const int max_timeout_in_seconds = INT_MAX / 1000; + if (max_timeout_in_seconds < timeout.total_seconds()) + return max_timeout_in_seconds * 1000; + else + return timeout.total_milliseconds(); + } + else + { + return 0; + } + } + + // Cancel all operations associated with the given descriptor. The do_cancel + // function of the handler objects will be invoked. This function does not + // acquire the epoll_reactor's mutex. + void cancel_ops_unlocked(socket_type descriptor) + { + bool interrupt = read_op_queue_.cancel_operations(descriptor); + interrupt = write_op_queue_.cancel_operations(descriptor) || interrupt; + interrupt = except_op_queue_.cancel_operations(descriptor) || interrupt; + if (interrupt) + interrupter_.interrupt(); + } + + // Mutex to protect access to internal data. + boost::asio::detail::mutex mutex_; + + // The epoll file descriptor. + int epoll_fd_; + + // Whether the epoll_wait call is currently in progress + bool wait_in_progress_; + + // The interrupter is used to break a blocking epoll_wait call. + select_interrupter interrupter_; + + // The queue of read operations. + reactor_op_queue read_op_queue_; + + // The queue of write operations. + reactor_op_queue write_op_queue_; + + // The queue of except operations. + reactor_op_queue except_op_queue_; + + // The queue of timers. + reactor_timer_queue timer_queue_; + + // The type for a map of descriptors that are registered with epoll. + typedef hash_map epoll_registration_map; + + // The map of descriptors that are registered with epoll. + epoll_registration_map epoll_registrations_; + + // The type for a map of descriptors to be cancelled. + typedef hash_map pending_cancellations_map; + + // The map of descriptors that are pending cancellation. + pending_cancellations_map pending_cancellations_; + + // Does the reactor loop thread need to stop. + bool stop_thread_; + + // The thread that is running the reactor loop. + boost::asio::detail::thread* thread_; +}; + +} // namespace detail +} // namespace asio +} // namespace boost + +#endif // LINUX_VERSION_CODE >= KERNEL_VERSION (2,5,45) +#endif // __linux__ + +#include + +#endif // BOOST_ASIO_DETAIL_EPOLL_REACTOR_HPP diff --git a/include/boost/asio/detail/event.hpp b/include/boost/asio/detail/event.hpp new file mode 100644 index 00000000..047d62f3 --- /dev/null +++ b/include/boost/asio/detail/event.hpp @@ -0,0 +1,52 @@ +// +// event.hpp +// ~~~~~~~~~ +// +// Copyright (c) 2003-2005 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_DETAIL_EVENT_HPP +#define BOOST_ASIO_DETAIL_EVENT_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include + +#include +#include +#include + +#if !defined(BOOST_HAS_THREADS) +# error Thread support is required! +#endif + +#if defined(BOOST_WINDOWS) +# include +#elif defined(BOOST_HAS_PTHREADS) +# include +#else +# error Only Windows and POSIX are supported! +#endif + +namespace boost { +namespace asio { +namespace detail { + +#if defined(BOOST_WINDOWS) +typedef win_event event; +#elif defined(BOOST_HAS_PTHREADS) +typedef posix_event event; +#endif + +} // namespace detail +} // namespace asio +} // namespace boost + +#include + +#endif // BOOST_ASIO_DETAIL_EVENT_HPP diff --git a/include/boost/asio/detail/fd_set_adapter.hpp b/include/boost/asio/detail/fd_set_adapter.hpp new file mode 100644 index 00000000..ddc40432 --- /dev/null +++ b/include/boost/asio/detail/fd_set_adapter.hpp @@ -0,0 +1,69 @@ +// +// fd_set_adapter.hpp +// ~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2005 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_DETAIL_FD_SET_ADAPTER_HPP +#define BOOST_ASIO_DETAIL_FD_SET_ADAPTER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include + +#include + +namespace boost { +namespace asio { +namespace detail { + +// Adapts the FD_SET type to meet the Descriptor_Set concept's requirements. +class fd_set_adapter +{ +public: + fd_set_adapter() + : max_descriptor_(invalid_socket) + { + FD_ZERO(&fd_set_); + } + + void set(socket_type descriptor) + { + if (max_descriptor_ == invalid_socket || descriptor > max_descriptor_) + max_descriptor_ = descriptor; + FD_SET(descriptor, &fd_set_); + } + + bool is_set(socket_type descriptor) const + { + return FD_ISSET(descriptor, &fd_set_) != 0; + } + + operator fd_set*() + { + return &fd_set_; + } + + socket_type max_descriptor() const + { + return max_descriptor_; + } + +private: + fd_set fd_set_; + socket_type max_descriptor_; +}; + +} // namespace detail +} // namespace asio +} // namespace boost + +#include + +#endif // BOOST_ASIO_DETAIL_FD_SET_ADAPTER_HPP diff --git a/include/boost/asio/detail/hash_map.hpp b/include/boost/asio/detail/hash_map.hpp new file mode 100644 index 00000000..b3ce143d --- /dev/null +++ b/include/boost/asio/detail/hash_map.hpp @@ -0,0 +1,191 @@ +// +// hash_map.hpp +// ~~~~~~~~~~~~ +// +// Copyright (c) 2003-2005 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_DETAIL_HASH_MAP_HPP +#define BOOST_ASIO_DETAIL_HASH_MAP_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include + +#include +#include +#include +#include +#include +#include + +#include + +namespace boost { +namespace asio { +namespace detail { + +template +class hash_map + : private noncopyable +{ +public: + // The type of a value in the map. + typedef std::pair value_type; + + // The type of a non-const iterator over the hash map. + typedef typename std::list::iterator iterator; + + // The type of a const iterator over the hash map. + typedef typename std::list::const_iterator const_iterator; + + // Constructor. + hash_map() + { + // Initialise all buckets to empty. + for (size_t i = 0; i < num_buckets; ++i) + buckets_[i].first = buckets_[i].last = values_.end(); + } + + // Get an iterator for the beginning of the map. + iterator begin() + { + return values_.begin(); + } + + // Get an iterator for the beginning of the map. + const_iterator begin() const + { + return values_.begin(); + } + + // Get an iterator for the end of the map. + iterator end() + { + return values_.end(); + } + + // Get an iterator for the end of the map. + const_iterator end() const + { + return values_.end(); + } + + // Find an entry in the map. + iterator find(const K& k) + { + size_t bucket = boost::hash_value(k) % num_buckets; + iterator it = buckets_[bucket].first; + if (it == values_.end()) + return values_.end(); + iterator end = buckets_[bucket].last; + ++end; + while (it != end) + { + if (it->first == k) + return it; + ++it; + } + return values_.end(); + } + + // Find an entry in the map. + const_iterator find(const K& k) const + { + size_t bucket = boost::hash_value(k) % num_buckets; + const_iterator it = buckets_[bucket].first; + if (it == values_.end()) + return it; + const_iterator end = buckets_[bucket].last; + ++end; + while (it != end) + { + if (it->first == k) + return it; + ++it; + } + return values_.end(); + } + + // Insert a new entry into the map. + std::pair insert(const value_type& v) + { + size_t bucket = boost::hash_value(v.first) % num_buckets; + iterator it = buckets_[bucket].first; + if (it == values_.end()) + { + buckets_[bucket].first = buckets_[bucket].last = + values_.insert(values_.end(), v); + return std::pair(buckets_[bucket].last, true); + } + iterator end = buckets_[bucket].last; + ++end; + while (it != end) + { + if (it->first == v.first) + return std::pair(it, false); + ++it; + } + buckets_[bucket].last = values_.insert(end, v); + return std::pair(buckets_[bucket].last, true); + } + + // Erase an entry from the map. + void erase(iterator it) + { + assert(it != values_.end()); + + size_t bucket = boost::hash_value(it->first) % num_buckets; + bool is_first = (it == buckets_[bucket].first); + bool is_last = (it == buckets_[bucket].last); + if (is_first && is_last) + buckets_[bucket].first = buckets_[bucket].last = values_.end(); + else if (is_first) + ++buckets_[bucket].first; + else if (is_last) + --buckets_[bucket].last; + + values_.erase(it); + } + + // Remove all entries from the map. + void clear() + { + // Initialise all buckets to empty. + for (size_t i = 0; i < num_buckets; ++i) + buckets_[i].first = buckets_[i].last = values_.end(); + + // Clear the values. + values_.clear(); + } + +private: + // The list of all values in the hash map. + std::list values_; + + // The type for a bucket in the hash table. + struct bucket_type + { + iterator first; + iterator last; + }; + + // The number of buckets in the hash. + enum { num_buckets = 1021 }; + + // The buckets in the hash. + bucket_type buckets_[num_buckets]; +}; + +} // namespace detail +} // namespace asio +} // namespace boost + +#include + +#endif // BOOST_ASIO_DETAIL_HASH_MAP_HPP diff --git a/include/boost/asio/detail/io_control.hpp b/include/boost/asio/detail/io_control.hpp new file mode 100644 index 00000000..f0dc3c5d --- /dev/null +++ b/include/boost/asio/detail/io_control.hpp @@ -0,0 +1,141 @@ +// +// io_control.hpp +// ~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2005 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_DETAIL_IO_CONTROL_HPP +#define BOOST_ASIO_DETAIL_IO_CONTROL_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include + +#include +#include +#include +#include + +#include + +namespace boost { +namespace asio { +namespace detail { +namespace io_control { + +// Helper template for implementing boolean-based IO control commands. +template +class boolean +{ +public: + // Default constructor. + boolean() + : value_(0) + { + } + + // Construct with a specific command value. + boolean(bool value) + : value_(value ? 1 : 0) + { + } + + // Get the name of the IO control command. + int name() const + { + return Name; + } + + // Set the value of the boolean. + void set(bool value) + { + value_ = value ? 1 : 0; + } + + // Get the current value of the boolean. + bool get() const + { + return value_ != 0; + } + + // Get the address of the command data. + detail::ioctl_arg_type* data() + { + return &value_; + } + + // Get the address of the command data. + const detail::ioctl_arg_type* data() const + { + return &value_; + } + +private: + detail::ioctl_arg_type value_; +}; + +// Helper template for implementing size-based IO control commands. +template +class size +{ +public: + // Default constructor. + size() + : value_(0) + { + } + + // Construct with a specific command value. + size(std::size_t value) + : value_(value) + { + } + + // Get the name of the IO control command. + int name() const + { + return Name; + } + + // Set the value of the size. + void set(std::size_t value) + { + value_ = static_cast(value); + } + + // Get the current value of the size. + std::size_t get() const + { + return static_cast(value_); + } + + // Get the address of the command data. + detail::ioctl_arg_type* data() + { + return &value_; + } + + // Get the address of the command data. + const detail::ioctl_arg_type* data() const + { + return &value_; + } + +private: + detail::ioctl_arg_type value_; +}; + +} // namespace io_control +} // namespace detail +} // namespace asio +} // namespace boost + +#include + +#endif // BOOST_ASIO_DETAIL_IO_CONTROL_HPP diff --git a/include/boost/asio/detail/kqueue_reactor.hpp b/include/boost/asio/detail/kqueue_reactor.hpp new file mode 100644 index 00000000..d007260c --- /dev/null +++ b/include/boost/asio/detail/kqueue_reactor.hpp @@ -0,0 +1,507 @@ +// +// kqueue_reactor.hpp +// ~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2005 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2005 Stefan Arentz (stefan at soze dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_DETAIL_KQUEUE_REACTOR_HPP +#define BOOST_ASIO_DETAIL_KQUEUE_REACTOR_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include + +#if defined(__MACH__) && defined(__APPLE__) + +// Define this to indicate that epoll is supported on the target platform. +#define BOOST_ASIO_HAS_KQUEUE_REACTOR 1 + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace boost { +namespace asio { +namespace detail { + +template +class kqueue_reactor + : private noncopyable +{ +public: + // Constructor. + template + kqueue_reactor(Demuxer&) + : mutex_(), + kqueue_fd_(do_kqueue_create()), + wait_in_progress_(false), + interrupter_(), + read_op_queue_(), + write_op_queue_(), + except_op_queue_(), + pending_cancellations_(), + stop_thread_(false), + thread_(0) + { + // Start the reactor's internal thread only if needed. + if (Own_Thread) + { + boost::asio::detail::signal_blocker sb; + thread_ = new boost::asio::detail::thread( + bind_handler(&kqueue_reactor::call_run_thread, this)); + } + + // Add the interrupter's descriptor to the kqueue. + struct kevent event; + EV_SET(&event, interrupter_.read_descriptor(), + EVFILT_READ, EV_ADD, 0, 0, 0); + ::kevent(kqueue_fd_, &event, 1, 0, 0, 0); + } + + // Destructor. + ~kqueue_reactor() + { + if (thread_) + { + boost::asio::detail::mutex::scoped_lock lock(mutex_); + stop_thread_ = true; + lock.unlock(); + interrupter_.interrupt(); + thread_->join(); + delete thread_; + } + + close(kqueue_fd_); + } + + // Start a new read operation. The handler object will be invoked when the + // given descriptor is ready to be read, or an error has occurred. + template + void start_read_op(socket_type descriptor, Handler handler) + { + boost::asio::detail::mutex::scoped_lock lock(mutex_); + + if (read_op_queue_.enqueue_operation(descriptor, handler)) + { + struct kevent event; + EV_SET(&event, descriptor, EVFILT_READ, EV_ADD, 0, 0, 0); + if (::kevent(kqueue_fd_, &event, 1, 0, 0, 0) == -1) + { + int error = errno; + read_op_queue_.dispatch_all_operations(descriptor, error); + } + } + } + + // Start a new write operation. The handler object will be invoked when the + // given descriptor is ready to be written, or an error has occurred. + template + void start_write_op(socket_type descriptor, Handler handler) + { + boost::asio::detail::mutex::scoped_lock lock(mutex_); + + if (write_op_queue_.enqueue_operation(descriptor, handler)) + { + struct kevent event; + EV_SET(&event, descriptor, EVFILT_WRITE, EV_ADD, 0, 0, 0); + if (::kevent(kqueue_fd_, &event, 1, 0, 0, 0) == -1) + { + int error = errno; + write_op_queue_.dispatch_all_operations(descriptor, error); + } + } + } + + // Start a new exception operation. The handler object will be invoked when + // the given descriptor has exception information, or an error has occurred. + template + void start_except_op(socket_type descriptor, Handler handler) + { + boost::asio::detail::mutex::scoped_lock lock(mutex_); + + if (except_op_queue_.enqueue_operation(descriptor, handler)) + { + struct kevent event; + if (read_op_queue_.has_operation(descriptor)) + EV_SET(&event, descriptor, EVFILT_READ, EV_ADD, 0, 0, 0); + else + EV_SET(&event, descriptor, EVFILT_READ, EV_ADD, EV_OOBAND, 0, 0); + if (::kevent(kqueue_fd_, &event, 1, 0, 0, 0) == -1) + { + int error = errno; + except_op_queue_.dispatch_all_operations(descriptor, error); + } + } + } + + // Start new write and exception operations. The handler object will be + // invoked when the given descriptor is ready for writing or has exception + // information available, or an error has occurred. + template + void start_write_and_except_ops(socket_type descriptor, Handler handler) + { + boost::asio::detail::mutex::scoped_lock lock(mutex_); + + if (write_op_queue_.enqueue_operation(descriptor, handler)) + { + struct kevent event; + EV_SET(&event, descriptor, EVFILT_WRITE, EV_ADD, 0, 0, 0); + if (::kevent(kqueue_fd_, &event, 1, 0, 0, 0) == -1) + { + int error = errno; + write_op_queue_.dispatch_all_operations(descriptor, error); + } + } + + if (except_op_queue_.enqueue_operation(descriptor, handler)) + { + struct kevent event; + if (read_op_queue_.has_operation(descriptor)) + EV_SET(&event, descriptor, EVFILT_READ, EV_ADD, 0, 0, 0); + else + EV_SET(&event, descriptor, EVFILT_READ, EV_ADD, EV_OOBAND, 0, 0); + if (::kevent(kqueue_fd_, &event, 1, 0, 0, 0) == -1) + { + int error = errno; + except_op_queue_.dispatch_all_operations(descriptor, error); + write_op_queue_.dispatch_all_operations(descriptor, error); + } + } + } + + // Cancel all operations associated with the given descriptor. The + // handlers associated with the descriptor will be invoked with the + // operation_aborted error. + void cancel_ops(socket_type descriptor) + { + boost::asio::detail::mutex::scoped_lock lock(mutex_); + cancel_ops_unlocked(descriptor); + } + + // Enqueue cancellation of all operations associated with the given + // descriptor. The handlers associated with the descriptor will be invoked + // with the operation_aborted error. This function does not acquire the + // select_reactor's mutex, and so should only be used from within a reactor + // handler. + void enqueue_cancel_ops_unlocked(socket_type descriptor) + { + pending_cancellations_.insert( + pending_cancellations_map::value_type(descriptor, true)); + } + + // Cancel any operations that are running against the descriptor and remove + // its registration from the reactor. + void close_descriptor(socket_type descriptor) + { + boost::asio::detail::mutex::scoped_lock lock(mutex_); + + // Remove the descriptor from kqueue. + struct kevent event[2]; + EV_SET(&event[0], descriptor, EVFILT_READ, EV_DELETE, 0, 0, 0); + EV_SET(&event[1], descriptor, EVFILT_WRITE, EV_DELETE, 0, 0, 0); + ::kevent(kqueue_fd_, event, 2, 0, 0, 0); + + // Cancel any outstanding operations associated with the descriptor. + cancel_ops_unlocked(descriptor); + } + + // Schedule a timer to expire at the specified absolute time. The + // do_operation function of the handler object will be invoked when the timer + // expires. Returns a token that may be used for cancelling the timer, but it + // is not valid after the timer expires. + template + void schedule_timer(const boost::posix_time::ptime& time, + Handler handler, void* token) + { + boost::asio::detail::mutex::scoped_lock lock(mutex_); + if (timer_queue_.enqueue_timer(time, handler, token)) + interrupter_.interrupt(); + } + + // Cancel the timer associated with the given token. Returns the number of + // handlers that have been posted or dispatched. + std::size_t cancel_timer(void* token) + { + boost::asio::detail::mutex::scoped_lock lock(mutex_); + return timer_queue_.cancel_timer(token); + } + +private: + friend class task_demuxer_service >; + + // Reset the select loop before a new run. + void reset() + { + boost::asio::detail::mutex::scoped_lock lock(mutex_); + stop_thread_ = false; + interrupter_.reset(); + } + + // Run the epoll loop. + void run() + { + boost::asio::detail::mutex::scoped_lock lock(mutex_); + + // Dispatch any operation cancellations that were made while the select + // loop was not running. + read_op_queue_.dispatch_cancellations(); + write_op_queue_.dispatch_cancellations(); + except_op_queue_.dispatch_cancellations(); + + bool stop = false; + while (!stop && !stop_thread_) + { + timespec timeout_buf; + timespec* timeout = get_timeout(timeout_buf); + wait_in_progress_ = true; + lock.unlock(); + + // Block on the kqueue descriptor. + struct kevent events[128]; + int num_events = kevent(kqueue_fd_, 0, 0, events, 128, timeout); + + lock.lock(); + wait_in_progress_ = false; + + // Block signals while dispatching operations. + boost::asio::detail::signal_blocker sb; + + // Dispatch the waiting events. + for (int i = 0; i < num_events; ++i) + { + int descriptor = events[i].ident; + if (descriptor == interrupter_.read_descriptor()) + { + stop = interrupter_.reset(); + } + else if (events[i].filter == EVFILT_READ) + { + // Dispatch operations associated with the descriptor. + bool more_reads = false; + bool more_except = false; + if (events[i].flags & EV_ERROR) + { + int error = events[i].data; + except_op_queue_.dispatch_all_operations(descriptor, error); + read_op_queue_.dispatch_all_operations(descriptor, error); + } + else if (events[i].flags & EV_OOBAND) + { + more_except = except_op_queue_.dispatch_operation(descriptor, 0); + if (events[i].data > 0) + more_reads = read_op_queue_.dispatch_operation(descriptor, 0); + else + more_reads = read_op_queue_.has_operation(descriptor); + } + else + { + more_reads = read_op_queue_.dispatch_operation(descriptor, 0); + more_except = except_op_queue_.has_operation(descriptor); + } + + // Update the descriptor in the kqueue. + struct kevent event; + if (more_reads) + EV_SET(&event, descriptor, EVFILT_READ, EV_ADD, 0, 0, 0); + else if (more_except) + EV_SET(&event, descriptor, EVFILT_READ, EV_ADD, EV_OOBAND, 0, 0); + else + EV_SET(&event, descriptor, EVFILT_READ, EV_DELETE, 0, 0, 0); + if (::kevent(kqueue_fd_, &event, 1, 0, 0, 0) == -1) + { + int error = errno; + except_op_queue_.dispatch_all_operations(descriptor, error); + read_op_queue_.dispatch_all_operations(descriptor, error); + } + } + else if (events[i].filter == EVFILT_WRITE) + { + // Dispatch operations associated with the descriptor. + bool more_writes = false; + if (events[i].flags & EV_ERROR) + { + int error = events[i].data; + write_op_queue_.dispatch_all_operations(descriptor, error); + } + else + { + more_writes = write_op_queue_.dispatch_operation(descriptor, 0); + } + + // Update the descriptor in the kqueue. + struct kevent event; + if (more_writes) + EV_SET(&event, descriptor, EVFILT_WRITE, EV_ADD, 0, 0, 0); + else + EV_SET(&event, descriptor, EVFILT_WRITE, EV_DELETE, 0, 0, 0); + if (::kevent(kqueue_fd_, &event, 1, 0, 0, 0) == -1) + { + int error = errno; + write_op_queue_.dispatch_all_operations(descriptor, error); + } + } + } + + read_op_queue_.dispatch_cancellations(); + write_op_queue_.dispatch_cancellations(); + except_op_queue_.dispatch_cancellations(); + timer_queue_.dispatch_timers( + boost::posix_time::microsec_clock::universal_time()); + + // Issue any pending cancellations. + pending_cancellations_map::iterator i = pending_cancellations_.begin(); + while (i != pending_cancellations_.end()) + { + cancel_ops_unlocked(i->first); + ++i; + } + pending_cancellations_.clear(); + } + } + + // Run the select loop in the thread. + void run_thread() + { + boost::asio::detail::mutex::scoped_lock lock(mutex_); + while (!stop_thread_) + { + lock.unlock(); + run(); + lock.lock(); + } + } + + // Entry point for the select loop thread. + static void call_run_thread(kqueue_reactor* reactor) + { + reactor->run_thread(); + } + + // Interrupt the select loop. + void interrupt() + { + interrupter_.interrupt(); + } + + // Create the kqueue file descriptor. Throws an exception if the descriptor + // cannot be created. + static int do_kqueue_create() + { + int fd = kqueue(); + if (fd == -1) + { + system_exception e("kqueue", errno); + boost::throw_exception(e); + } + return fd; + } + + // Get the timeout value for the kevent call. + timespec* get_timeout(timespec& ts) + { + if (timer_queue_.empty()) + return 0; + + boost::posix_time::ptime now + = boost::posix_time::microsec_clock::universal_time(); + boost::posix_time::ptime earliest_timer; + timer_queue_.get_earliest_time(earliest_timer); + if (now < earliest_timer) + { + boost::posix_time::time_duration timeout = earliest_timer - now; + ts.tv_sec = timeout.total_seconds(); + ts.tv_nsec = timeout.total_nanoseconds() % 1000000000; + } + else + { + ts.tv_sec = 0; + ts.tv_nsec = 0; + } + + return &ts; + } + + // Cancel all operations associated with the given descriptor. The do_cancel + // function of the handler objects will be invoked. This function does not + // acquire the epoll_reactor's mutex. + void cancel_ops_unlocked(socket_type descriptor) + { + bool interrupt = read_op_queue_.cancel_operations(descriptor); + interrupt = write_op_queue_.cancel_operations(descriptor) || interrupt; + interrupt = except_op_queue_.cancel_operations(descriptor) || interrupt; + if (interrupt) + interrupter_.interrupt(); + } + + // Mutex to protect access to internal data. + boost::asio::detail::mutex mutex_; + + // The epoll file descriptor. + int kqueue_fd_; + + // Whether the kqueue wait call is currently in progress + bool wait_in_progress_; + + // The interrupter is used to break a blocking epoll_wait call. + select_interrupter interrupter_; + + // The queue of read operations. + reactor_op_queue read_op_queue_; + + // The queue of write operations. + reactor_op_queue write_op_queue_; + + // The queue of except operations. + reactor_op_queue except_op_queue_; + + // The queue of timers. + reactor_timer_queue timer_queue_; + + // The type for a map of descriptors to be cancelled. + typedef hash_map pending_cancellations_map; + + // The map of descriptors that are pending cancellation. + pending_cancellations_map pending_cancellations_; + + // Does the reactor loop thread need to stop. + bool stop_thread_; + + // The thread that is running the reactor loop. + boost::asio::detail::thread* thread_; +}; + +} // namespace detail +} // namespace asio +} // namespace boost + +#endif // defined(__MACH__) && defined(__APPLE__) + +#include + +#endif // BOOST_ASIO_DETAIL_KQUEUE_REACTOR_HPP diff --git a/include/boost/asio/detail/locking_dispatcher_impl.hpp b/include/boost/asio/detail/locking_dispatcher_impl.hpp new file mode 100644 index 00000000..2effa0de --- /dev/null +++ b/include/boost/asio/detail/locking_dispatcher_impl.hpp @@ -0,0 +1,222 @@ +// +// locking_dispatcher_impl.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2005 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_DETAIL_LOCKING_DISPATCHER_IMPL_HPP +#define BOOST_ASIO_DETAIL_LOCKING_DISPATCHER_IMPL_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include + +#include +#include +#include + +namespace boost { +namespace asio { +namespace detail { + +template +class locking_dispatcher_impl + : private noncopyable +{ +public: + // Constructor. + locking_dispatcher_impl() + : first_waiter_(0), + last_waiter_(0), + mutex_() + { + } + + // Request a dispatcher to invoke the given handler. + template + void dispatch(Demuxer& demuxer, Handler handler) + { + boost::asio::detail::mutex::scoped_lock lock(mutex_); + + if (first_waiter_ == 0) + { + // This handler now has the lock, so can be dispatched immediately. + first_waiter_ = last_waiter_ = new waiter(handler); + lock.unlock(); + demuxer.dispatch(waiter_handler(demuxer, *this)); + } + else + { + // Another waiter already holds the lock, so this handler must join + // the list of waiters. The handler will be posted automatically when + // its turn comes. + last_waiter_->next_ = new waiter(handler); + last_waiter_ = last_waiter_->next_; + } + } + + // Request a dispatcher to invoke the given handler and return immediately. + template + void post(Demuxer& demuxer, Handler handler) + { + boost::asio::detail::mutex::scoped_lock lock(mutex_); + + if (first_waiter_ == 0) + { + // This handler now has the lock, so can be posted immediately. + first_waiter_ = last_waiter_ = new waiter(handler); + lock.unlock(); + demuxer.post(waiter_handler(demuxer, *this)); + } + else + { + // Another waiter already holds the lock, so this handler must join + // the list of waiters. The handler will be posted automatically when + // its turn comes. + last_waiter_->next_ = new waiter(handler); + last_waiter_ = last_waiter_->next_; + } + } + +private: + // Base class for all waiter types. + class waiter_base + { + public: + waiter_base() + : next_(0) + { + } + + virtual ~waiter_base() + { + } + + virtual void call() = 0; + + waiter_base* next_; + }; + + // Class template for a waiter. + template + class waiter + : public waiter_base + { + public: + waiter(Handler handler) + : handler_(handler) + { + } + + virtual void call() + { + handler_(); + } + + private: + Handler handler_; + }; + + // Helper class to allow waiting handlers to be dispatched. + class waiter_handler + { + public: + waiter_handler(Demuxer& demuxer, locking_dispatcher_impl& impl) + : demuxer_(demuxer), + impl_(impl) + { + } + + // Helper class to automatically enqueue next waiter on block exit. This + // class cannot be function-local to operator() due to a linker bug in MSVC, + // where an inline-member of a function-local class is exported by each .obj + // file that includes the header. + class post_next_waiter_on_exit + { + public: + post_next_waiter_on_exit(waiter_handler& handler) + : handler_(handler) + { + } + + ~post_next_waiter_on_exit() + { + handler_.post_next_waiter(); + } + + private: + waiter_handler& handler_; + }; + + void operator()() + { + post_next_waiter_on_exit p(*this); + + // Call the handler. + impl_.first_waiter_->call(); + } + + void post_next_waiter() + { + boost::asio::detail::mutex::scoped_lock lock(impl_.mutex_); + + waiter_base* tmp = impl_.first_waiter_; + impl_.first_waiter_ = impl_.first_waiter_->next_; + if (impl_.first_waiter_) + { + lock.unlock(); + + // Ensure the waiter is not deleted until after we have finished + // accessing the dispatcher, since the waiter might indirectly own + // the dispatcher and so destroying the waiter will cause the + // dispatcher to be destroyed. + delete tmp; + + // There is more work to do, so post this handler again. + demuxer_.post(*this); + } + else + { + impl_.last_waiter_ = 0; + + lock.unlock(); + + // Ensure the waiter is not deleted until after we have finished + // accessing the dispatcher, since the waiter might indirectly own + // the dispatcher and so destroying the waiter will cause the + // dispatcher to be destroyed. + delete tmp; + } + } + + private: + Demuxer& demuxer_; + locking_dispatcher_impl& impl_; + }; + + friend class waiter_handler; + + // The start of the list of waiters for the dispatcher. If this pointer + // is non-null then it indicates that a handler holds the lock. + waiter_base* first_waiter_; + + // The end of the list of waiters for the dispatcher. + waiter_base* last_waiter_; + + // Mutex to protect access to internal data. + boost::asio::detail::mutex mutex_; +}; + +} // namespace detail +} // namespace asio +} // namespace boost + +#include + +#endif // BOOST_ASIO_DETAIL_LOCKING_DISPATCHER_IMPL_HPP diff --git a/include/boost/asio/detail/locking_dispatcher_service.hpp b/include/boost/asio/detail/locking_dispatcher_service.hpp new file mode 100644 index 00000000..4b7fe2bc --- /dev/null +++ b/include/boost/asio/detail/locking_dispatcher_service.hpp @@ -0,0 +1,95 @@ +// +// locking_dispatcher_service.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2005 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_DETAIL_LOCKING_DISPATCHER_SERVICE_HPP +#define BOOST_ASIO_DETAIL_LOCKING_DISPATCHER_SERVICE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include + +#include + +namespace boost { +namespace asio { +namespace detail { + +template +class locking_dispatcher_service +{ +public: + // The native type of the locking dispatcher. + typedef locking_dispatcher_impl* impl_type; + + // Return a null locking dispatcher implementation. + static impl_type null() + { + return 0; + } + + // Constructor. + locking_dispatcher_service(Demuxer& d) + : demuxer_(d) + { + } + + // The demuxer type for this service. + typedef Demuxer demuxer_type; + + // Get the demuxer associated with the service. + demuxer_type& demuxer() + { + return demuxer_; + } + + // Create a new locking dispatcher implementation. + void create(impl_type& impl) + { + impl = new locking_dispatcher_impl; + } + + // Destroy a locking dispatcher implementation. + void destroy(impl_type& impl) + { + if (impl != null()) + { + delete impl; + impl = null(); + } + } + + // Request a dispatcher to invoke the given handler. + template + void dispatch(impl_type& impl, Handler handler) + { + impl->dispatch(demuxer_, handler); + } + + // Request a dispatcher to invoke the given handler and return immediately. + template + void post(impl_type& impl, Handler handler) + { + impl->post(demuxer_, handler); + } + +private: + // The demuxer used for dispatching handlers. + Demuxer& demuxer_; +}; + +} // namespace detail +} // namespace asio +} // namespace boost + +#include + +#endif // BOOST_ASIO_DETAIL_LOCKING_DISPATCHER_SERVICE_HPP diff --git a/include/boost/asio/detail/mutex.hpp b/include/boost/asio/detail/mutex.hpp new file mode 100644 index 00000000..ac48c63a --- /dev/null +++ b/include/boost/asio/detail/mutex.hpp @@ -0,0 +1,52 @@ +// +// mutex.hpp +// ~~~~~~~~~ +// +// Copyright (c) 2003-2005 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_DETAIL_MUTEX_HPP +#define BOOST_ASIO_DETAIL_MUTEX_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include + +#include +#include +#include + +#if !defined(BOOST_HAS_THREADS) +# error Thread support is required! +#endif + +#if defined(BOOST_WINDOWS) +# include +#elif defined(BOOST_HAS_PTHREADS) +# include +#else +# error Only Windows and POSIX are supported! +#endif + +namespace boost { +namespace asio { +namespace detail { + +#if defined(BOOST_WINDOWS) +typedef win_mutex mutex; +#elif defined(BOOST_HAS_PTHREADS) +typedef posix_mutex mutex; +#endif + +} // namespace detail +} // namespace asio +} // namespace boost + +#include + +#endif // BOOST_ASIO_DETAIL_MUTEX_HPP diff --git a/include/boost/asio/detail/noncopyable.hpp b/include/boost/asio/detail/noncopyable.hpp new file mode 100644 index 00000000..a0f76966 --- /dev/null +++ b/include/boost/asio/detail/noncopyable.hpp @@ -0,0 +1,57 @@ +// +// noncopyable.hpp +// ~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2005 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_DETAIL_NONCOPYABLE_HPP +#define BOOST_ASIO_DETAIL_NONCOPYABLE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include + +#include +#include +#include +#include +#include + +namespace boost { +namespace asio { +namespace detail { + +#if BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564)) +// Redefine the noncopyable class for Borland C++ since that compiler does not +// apply the empty base optimisation unless the base class contains a dummy +// char data member. +class noncopyable +{ +protected: + noncopyable() {} + ~noncopyable() {} +private: + noncopyable(const noncopyable&); + const noncopyable& operator=(const noncopyable&); + char dummy_; +}; +#else +using boost::noncopyable; +#endif + +} // namespace detail + +using boost::asio::detail::noncopyable; + +} // namespace asio +} // namespace boost + +#include + +#endif // BOOST_ASIO_DETAIL_NONCOPYABLE_HPP diff --git a/include/boost/asio/detail/pipe_select_interrupter.hpp b/include/boost/asio/detail/pipe_select_interrupter.hpp new file mode 100644 index 00000000..2e419b24 --- /dev/null +++ b/include/boost/asio/detail/pipe_select_interrupter.hpp @@ -0,0 +1,106 @@ +// +// pipe_select_interrupter.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2005 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_DETAIL_PIPE_SELECT_INTERRUPTER_HPP +#define BOOST_ASIO_DETAIL_PIPE_SELECT_INTERRUPTER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include + +#include +#include +#include + +#if !defined(BOOST_WINDOWS) + +#include +#include +#include + +#include + +namespace boost { +namespace asio { +namespace detail { + +class pipe_select_interrupter +{ +public: + // Constructor. + pipe_select_interrupter() + { + int pipe_fds[2]; + if (pipe(pipe_fds) == 0) + { + read_descriptor_ = pipe_fds[0]; + ::fcntl(read_descriptor_, F_SETFL, O_NONBLOCK); + write_descriptor_ = pipe_fds[1]; + ::fcntl(write_descriptor_, F_SETFL, O_NONBLOCK); + } + } + + // Destructor. + ~pipe_select_interrupter() + { + if (read_descriptor_ != -1) + ::close(read_descriptor_); + if (write_descriptor_ != -1) + ::close(write_descriptor_); + } + + // Interrupt the select call. + void interrupt() + { + char byte = 0; + ::write(write_descriptor_, &byte, 1); + } + + // Reset the select interrupt. Returns true if the call was interrupted. + bool reset() + { + char data[1024]; + int bytes_read = ::read(read_descriptor_, data, sizeof(data)); + bool was_interrupted = (bytes_read > 0); + while (bytes_read == sizeof(data)) + bytes_read = ::read(read_descriptor_, data, sizeof(data)); + return was_interrupted; + } + + // Get the read descriptor to be passed to select. + int read_descriptor() const + { + return read_descriptor_; + } + +private: + // The read end of a connection used to interrupt the select call. This file + // descriptor is passed to select such that when it is time to stop, a single + // byte will be written on the other end of the connection and this + // descriptor will become readable. + int read_descriptor_; + + // The write end of a connection used to interrupt the select call. A single + // byte may be written to this to wake up the select which is waiting for the + // other end to become readable. + int write_descriptor_; +}; + +} // namespace detail +} // namespace asio +} // namespace boost + +#endif // !defined(BOOST_WINDOWS) + +#include + +#endif // BOOST_ASIO_DETAIL_PIPE_SELECT_INTERRUPTER_HPP diff --git a/include/boost/asio/detail/pop_options.hpp b/include/boost/asio/detail/pop_options.hpp new file mode 100644 index 00000000..c62d1bfe --- /dev/null +++ b/include/boost/asio/detail/pop_options.hpp @@ -0,0 +1,88 @@ +// +// pop_options.hpp +// ~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2005 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +// No header guard + +#if defined(__COMO__) + +// Comeau C++ + +#elif defined(__DMC__) + +// Digital Mars C++ + +#elif defined(__INTEL_COMPILER) || defined(__ICL) \ + || defined(__ICC) || defined(__ECC) + +// Intel C++ + +#elif defined(__GNUC__) + +// GNU C++ + +# if defined (__MINGW32__) +# pragma pack (pop) +# endif + +#elif defined(__KCC) + +// Kai C++ + +#elif defined(__sgi) + +// SGI MIPSpro C++ + +#elif defined(__DECCXX) + +// Compaq Tru64 Unix cxx + +#elif defined(__ghs) + +// Greenhills C++ + +#elif defined(__BORLANDC__) + +// Borland C++ + +# pragma option pop +# pragma nopushoptwarn +# pragma nopackwarning + +#elif defined(__MWERKS__) + +// Metrowerks CodeWarrior + +#elif defined(__SUNPRO_CC) + +// Sun Workshop Compiler C++ + +#elif defined(__HP_aCC) + +// HP aCC + +#elif defined(__MRC__) || defined(__SC__) + +// MPW MrCpp or SCpp + +#elif defined(__IBMCPP__) + +// IBM Visual Age + +#elif defined(_MSC_VER) + +// Microsoft Visual C++ +// +// Must remain the last #elif since some other vendors (Metrowerks, for example) +// also #define _MSC_VER + +# pragma warning (pop) +# pragma pack (pop) + +#endif diff --git a/include/boost/asio/detail/posix_event.hpp b/include/boost/asio/detail/posix_event.hpp new file mode 100644 index 00000000..799d7e2a --- /dev/null +++ b/include/boost/asio/detail/posix_event.hpp @@ -0,0 +1,109 @@ +// +// posix_event.hpp +// ~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2005 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_DETAIL_POSIX_EVENT_HPP +#define BOOST_ASIO_DETAIL_POSIX_EVENT_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include + +#include +#include +#include + +#if defined(BOOST_HAS_PTHREADS) + +#include +#include +#include +#include + +#include +#include + +namespace boost { +namespace asio { +namespace detail { + +class posix_event + : private noncopyable +{ +public: + // Constructor. + posix_event() + : signalled_(false) + { + int error = ::pthread_mutex_init(&mutex_, 0); + if (error != 0) + { + system_exception e("event", error); + boost::throw_exception(e); + } + + error = ::pthread_cond_init(&cond_, 0); + if (error != 0) + { + ::pthread_mutex_destroy(&mutex_); + system_exception e("event", error); + boost::throw_exception(e); + } + } + + // Destructor. + ~posix_event() + { + ::pthread_cond_destroy(&cond_); + ::pthread_mutex_destroy(&mutex_); + } + + // Signal the event. + void signal() + { + ::pthread_mutex_lock(&mutex_); // Ignore EINVAL and EDEADLK. + signalled_ = true; + ::pthread_cond_signal(&cond_); // Ignore EINVAL. + ::pthread_mutex_unlock(&mutex_); // Ignore EINVAL and EPERM. + } + + // Reset the event. + void clear() + { + ::pthread_mutex_lock(&mutex_); // Ignore EINVAL and EDEADLK. + signalled_ = false; + ::pthread_mutex_unlock(&mutex_); // Ignore EINVAL and EPERM. + } + + // Wait for the event to become signalled. + void wait() + { + ::pthread_mutex_lock(&mutex_); // Ignore EINVAL and EDEADLK. + while (!signalled_) + ::pthread_cond_wait(&cond_, &mutex_); // Ignore EINVAL. + ::pthread_mutex_unlock(&mutex_); // Ignore EINVAL and EPERM. + } + +private: + ::pthread_mutex_t mutex_; + ::pthread_cond_t cond_; + bool signalled_; +}; + +} // namespace detail +} // namespace asio +} // namespace boost + +#endif // defined(BOOST_HAS_PTHREADS) + +#include + +#endif // BOOST_ASIO_DETAIL_POSIX_EVENT_HPP diff --git a/include/boost/asio/detail/posix_mutex.hpp b/include/boost/asio/detail/posix_mutex.hpp new file mode 100644 index 00000000..dde94030 --- /dev/null +++ b/include/boost/asio/detail/posix_mutex.hpp @@ -0,0 +1,96 @@ +// +// posix_mutex.hpp +// ~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2005 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_DETAIL_POSIX_MUTEX_HPP +#define BOOST_ASIO_DETAIL_POSIX_MUTEX_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include + +#include +#include +#include + +#if defined(BOOST_HAS_PTHREADS) + +#include +#include +#include +#include + +#include +#include +#include + +namespace boost { +namespace asio { +namespace detail { + +class posix_mutex + : private noncopyable +{ +public: + typedef boost::asio::detail::scoped_lock scoped_lock; + + // Constructor. + posix_mutex() + { + int error = ::pthread_mutex_init(&mutex_, 0); + if (error != 0) + { + system_exception e("mutex", error); + boost::throw_exception(e); + } + } + + // Destructor. + ~posix_mutex() + { + ::pthread_mutex_destroy(&mutex_); + } + + // Lock the mutex. + void lock() + { + int error = ::pthread_mutex_lock(&mutex_); + if (error != 0) + { + system_exception e("mutex", error); + boost::throw_exception(e); + } + } + + // Unlock the mutex. + void unlock() + { + int error = ::pthread_mutex_unlock(&mutex_); + if (error != 0) + { + system_exception e("mutex", error); + boost::throw_exception(e); + } + } + +private: + ::pthread_mutex_t mutex_; +}; + +} // namespace detail +} // namespace asio +} // namespace boost + +#endif // defined(BOOST_HAS_PTHREADS) + +#include + +#endif // BOOST_ASIO_DETAIL_POSIX_MUTEX_HPP diff --git a/include/boost/asio/detail/posix_signal_blocker.hpp b/include/boost/asio/detail/posix_signal_blocker.hpp new file mode 100644 index 00000000..f17cd4e0 --- /dev/null +++ b/include/boost/asio/detail/posix_signal_blocker.hpp @@ -0,0 +1,91 @@ +// +// posix_signal_blocker.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2005 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_DETAIL_POSIX_SIGNAL_BLOCKER_HPP +#define BOOST_ASIO_DETAIL_POSIX_SIGNAL_BLOCKER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include + +#include +#include +#include + +#if defined(BOOST_HAS_PTHREADS) + +#include +#include +#include +#include + +#include + +namespace boost { +namespace asio { +namespace detail { + +class posix_signal_blocker + : private noncopyable +{ +public: + // Constructor blocks all signals for the calling thread. + posix_signal_blocker() + : blocked_(false) + { + sigset_t new_mask; + sigfillset(&new_mask); + blocked_ = (pthread_sigmask(SIG_BLOCK, &new_mask, &old_mask_) == 0); + } + + // Destructor restores the previous signal mask. + ~posix_signal_blocker() + { + if (blocked_) + pthread_sigmask(SIG_SETMASK, &old_mask_, 0); + } + + // Block all signals for the calling thread. + void block() + { + if (!blocked_) + { + sigset_t new_mask; + sigfillset(&new_mask); + blocked_ = (pthread_sigmask(SIG_BLOCK, &new_mask, &old_mask_) == 0); + } + } + + // Restore the previous signal mask. + void unblock() + { + if (blocked_) + blocked_ = (pthread_sigmask(SIG_SETMASK, &old_mask_, 0) != 0); + } + +private: + // Have signals been blocked. + bool blocked_; + + // The previous signal mask. + sigset_t old_mask_; +}; + +} // namespace detail +} // namespace asio +} // namespace boost + +#endif // defined(BOOST_HAS_PTHREADS) + +#include + +#endif // BOOST_ASIO_DETAIL_POSIX_SIGNAL_BLOCKER_HPP diff --git a/include/boost/asio/detail/posix_thread.hpp b/include/boost/asio/detail/posix_thread.hpp new file mode 100644 index 00000000..1a9921b1 --- /dev/null +++ b/include/boost/asio/detail/posix_thread.hpp @@ -0,0 +1,127 @@ +// +// posix_thread.hpp +// ~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2005 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_DETAIL_POSIX_THREAD_HPP +#define BOOST_ASIO_DETAIL_POSIX_THREAD_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include + +#include +#include +#include + +#if defined(BOOST_HAS_PTHREADS) + +#include +#include +#include +#include +#include + +#include +#include + +namespace boost { +namespace asio { +namespace detail { + +extern "C" void* asio_detail_posix_thread_function(void* arg); + +class posix_thread + : private noncopyable +{ +public: + // Constructor. + template + posix_thread(Function f) + : joined_(false) + { + std::auto_ptr arg(new func(f)); + int error = ::pthread_create(&thread_, 0, + asio_detail_posix_thread_function, arg.get()); + if (error != 0) + { + system_exception e("thread", error); + boost::throw_exception(e); + } + arg.release(); + } + + // Destructor. + ~posix_thread() + { + if (!joined_) + ::pthread_detach(thread_); + } + + // Wait for the thread to exit. + void join() + { + if (!joined_) + { + ::pthread_join(thread_, 0); + joined_ = true; + } + } + +private: + friend void* asio_detail_posix_thread_function(void* arg); + + class func_base + { + public: + virtual ~func_base() {} + virtual void run() = 0; + }; + + template + class func + : public func_base + { + public: + func(Function f) + : f_(f) + { + } + + virtual void run() + { + f_(); + } + + private: + Function f_; + }; + + ::pthread_t thread_; + bool joined_; +}; + +inline void* asio_detail_posix_thread_function(void* arg) +{ + std::auto_ptr f( + static_cast(arg)); + f->run(); + return 0; +} + +} // namespace detail +} // namespace asio +} // namespace boost + +#endif // defined(BOOST_HAS_PTHREADS) + +#include + +#endif // BOOST_ASIO_DETAIL_POSIX_THREAD_HPP diff --git a/include/boost/asio/detail/posix_tss_ptr.hpp b/include/boost/asio/detail/posix_tss_ptr.hpp new file mode 100644 index 00000000..f4814e63 --- /dev/null +++ b/include/boost/asio/detail/posix_tss_ptr.hpp @@ -0,0 +1,86 @@ +// +// posix_tss_ptr.hpp +// ~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2005 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_DETAIL_POSIX_TSS_PTR_HPP +#define BOOST_ASIO_DETAIL_POSIX_TSS_PTR_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include + +#include +#include +#include + +#if defined(BOOST_HAS_PTHREADS) + +#include +#include +#include +#include + +#include +#include + +namespace boost { +namespace asio { +namespace detail { + +template +class posix_tss_ptr + : private noncopyable +{ +public: + // Constructor. + posix_tss_ptr() + { + int error = ::pthread_key_create(&tss_key_, 0); + if (error != 0) + { + system_exception e("tss", error); + boost::throw_exception(e); + } + } + + // Destructor. + ~posix_tss_ptr() + { + ::pthread_key_delete(tss_key_); + } + + // Get the value. + operator T*() const + { + return static_cast(::pthread_getspecific(tss_key_)); + } + + // Set the value. + void operator=(T* value) + { + ::pthread_setspecific(tss_key_, value); + } + +private: + // Thread-specific storage to allow unlocked access to determine whether a + // thread is a member of the pool. + pthread_key_t tss_key_; +}; + +} // namespace detail +} // namespace asio +} // namespace boost + +#endif // defined(BOOST_HAS_PTHREADS) + +#include + +#endif // BOOST_ASIO_DETAIL_POSIX_TSS_PTR_HPP diff --git a/include/boost/asio/detail/push_options.hpp b/include/boost/asio/detail/push_options.hpp new file mode 100644 index 00000000..de4718e0 --- /dev/null +++ b/include/boost/asio/detail/push_options.hpp @@ -0,0 +1,105 @@ +// +// push_options.hpp +// ~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2005 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +// No header guard + +#if defined(__COMO__) + +// Comeau C++ + +#elif defined(__DMC__) + +// Digital Mars C++ + +#elif defined(__INTEL_COMPILER) || defined(__ICL) \ + || defined(__ICC) || defined(__ECC) + +// Intel C++ + +#elif defined(__GNUC__) + +// GNU C++ + +# if defined (__MINGW32__) +# pragma pack (push, 8) +# endif + +#elif defined(__KCC) + +// Kai C++ + +#elif defined(__sgi) + +// SGI MIPSpro C++ + +#elif defined(__DECCXX) + +// Compaq Tru64 Unix cxx + +#elif defined(__ghs) + +// Greenhills C++ + +#elif defined(__BORLANDC__) + +// Borland C++ + +# pragma option push -a8 -b -Ve- -Vx- -w-inl +# pragma nopushoptwarn +# pragma nopackwarning +# if !defined(__MT__) +# error Multithreaded RTL must be selected. +# endif // !defined(__MT__) + +#elif defined(__MWERKS__) + +// Metrowerks CodeWarrior + +#elif defined(__SUNPRO_CC) + +// Sun Workshop Compiler C++ + +#elif defined(__HP_aCC) + +// HP aCC + +#elif defined(__MRC__) || defined(__SC__) + +// MPW MrCpp or SCpp + +#elif defined(__IBMCPP__) + +// IBM Visual Age + +#elif defined(_MSC_VER) + +// Microsoft Visual C++ +// +// Must remain the last #elif since some other vendors (Metrowerks, for example) +// also #define _MSC_VER + +# pragma warning (disable:4103) +# pragma warning (push) +# pragma warning (disable:4244) +# pragma warning (disable:4355) +# pragma pack (push, 8) +// Note that if the /Og optimisation flag is enabled with MSVC6, the compiler +// has a tendency to incorrectly optimise away some calls to member template +// functions, even though those functions contain code that should not be +// optimised away! Therefore we will always disable this optimisation option +// for the MSVC6 compiler. +# if (_MSC_VER < 1300) +# pragma optimize ("g", off) +# endif +# if !defined(_MT) +# error Multithreaded RTL must be selected. +# endif // !defined(_MT) + +#endif diff --git a/include/boost/asio/detail/reactive_deadline_timer_service.hpp b/include/boost/asio/detail/reactive_deadline_timer_service.hpp new file mode 100644 index 00000000..e7317f40 --- /dev/null +++ b/include/boost/asio/detail/reactive_deadline_timer_service.hpp @@ -0,0 +1,190 @@ +// +// reactive_deadline_timer_service.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2005 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_DETAIL_REACTIVE_DEADLINE_TIMER_SERVICE_HPP +#define BOOST_ASIO_DETAIL_REACTIVE_DEADLINE_TIMER_SERVICE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace boost { +namespace asio { +namespace detail { + +template +class reactive_deadline_timer_service +{ +public: + // Implementation structure for a timer. + struct timer_impl + : private noncopyable + { + boost::posix_time::ptime expiry; + }; + + // The native type of the timer. This type is dependent on the underlying + // implementation of the timer service. + typedef timer_impl* impl_type; + + // The demuxer type for this service. + typedef Demuxer demuxer_type; + + // The time type. + typedef typename Time_Traits::time_type time_type; + + // The duration type. + typedef typename Time_Traits::duration_type duration_type; + + // Constructor. + reactive_deadline_timer_service(demuxer_type& d) + : demuxer_(d), + reactor_(d.get_service(service_factory())) + { + } + + // Get the demuxer associated with the service. + demuxer_type& demuxer() + { + return demuxer_; + } + + // Return a null timer implementation. + static impl_type null() + { + return 0; + } + + // Create a new timer implementation. + void create(impl_type& impl) + { + impl = new timer_impl; + } + + // Destroy a timer implementation. + void destroy(impl_type& impl) + { + if (impl != null()) + { + reactor_.cancel_timer(impl); + delete impl; + impl = null(); + } + } + + // Get the expiry time for the timer as an absolute time. + time_type expires_at(const impl_type& impl) const + { + return Time_Traits::from_utc(impl->expiry); + } + + // Set the expiry time for the timer as an absolute time. + void expires_at(impl_type& impl, const time_type& expiry_time) + { + impl->expiry = Time_Traits::to_utc(expiry_time); + } + + // Get the expiry time for the timer relative to now. + duration_type expires_from_now(const impl_type& impl) const + { + return Time_Traits::subtract(expires_at(impl), Time_Traits::now()); + } + + // Set the expiry time for the timer relative to now. + void expires_from_now(impl_type& impl, const duration_type& expiry_time) + { + expires_at(impl, Time_Traits::add(Time_Traits::now(), expiry_time)); + } + + // Cancel any asynchronous wait operations associated with the timer. + std::size_t cancel(impl_type& impl) + { + return reactor_.cancel_timer(impl); + } + + // Perform a blocking wait on the timer. + void wait(impl_type& impl) + { + boost::posix_time::ptime now + = boost::posix_time::microsec_clock::universal_time(); + while (now < impl->expiry) + { + boost::posix_time::time_duration timeout = impl->expiry - now; + ::timeval tv; + tv.tv_sec = timeout.total_seconds(); + tv.tv_usec = timeout.total_microseconds() % 1000000; + socket_ops::select(0, 0, 0, 0, &tv); + now = boost::posix_time::microsec_clock::universal_time(); + } + } + + template + class wait_handler + { + public: + wait_handler(impl_type& impl, Demuxer& demuxer, Handler handler) + : impl_(impl), + demuxer_(demuxer), + work_(demuxer), + handler_(handler) + { + } + + void operator()(int result) + { + boost::asio::error e(result); + demuxer_.post(detail::bind_handler(handler_, e)); + } + + private: + impl_type& impl_; + Demuxer& demuxer_; + typename Demuxer::work work_; + Handler handler_; + }; + + // Start an asynchronous wait on the timer. + template + void async_wait(impl_type& impl, Handler handler) + { + reactor_.schedule_timer(impl->expiry, + wait_handler(impl, demuxer_, handler), impl); + } + +private: + // The demuxer used for dispatching handlers. + Demuxer& demuxer_; + + // The selector that performs event demultiplexing for the provider. + Reactor& reactor_; +}; + +} // namespace detail +} // namespace asio +} // namespace boost + +#include + +#endif // BOOST_ASIO_DETAIL_REACTIVE_DEADLINE_TIMER_SERVICE_HPP diff --git a/include/boost/asio/detail/reactive_socket_service.hpp b/include/boost/asio/detail/reactive_socket_service.hpp new file mode 100644 index 00000000..29d30f91 --- /dev/null +++ b/include/boost/asio/detail/reactive_socket_service.hpp @@ -0,0 +1,995 @@ +// +// reactive_socket_service.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2005 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_DETAIL_REACTIVE_SOCKET_SERVICE_HPP +#define BOOST_ASIO_DETAIL_REACTIVE_SOCKET_SERVICE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace boost { +namespace asio { +namespace detail { + +template +class reactive_socket_service +{ +public: + // The native type of the socket. This type is dependent on the + // underlying implementation of the socket layer. + typedef socket_type impl_type; + + // The maximum number of buffers to support in a single operation. + enum { max_buffers = 16 }; + + // Constructor. + reactive_socket_service(Demuxer& d) + : demuxer_(d), + reactor_(d.get_service(service_factory())) + { + } + + // The demuxer type for this service. + typedef Demuxer demuxer_type; + + // Get the demuxer associated with the service. + demuxer_type& demuxer() + { + return demuxer_; + } + + // Return a null socket implementation. + static impl_type null() + { + return invalid_socket; + } + + // Open a new socket implementation. + template + void open(impl_type& impl, const Protocol& protocol, + Error_Handler error_handler) + { + socket_holder sock(socket_ops::socket(protocol.family(), + protocol.type(), protocol.protocol())); + if (sock.get() == invalid_socket) + error_handler(boost::asio::error(socket_ops::get_error())); + else + impl = sock.release(); + } + + // Assign a new socket implementation. + void assign(impl_type& impl, impl_type new_impl) + { + impl = new_impl; + } + + // Destroy a socket implementation. + template + void close(impl_type& impl, Error_Handler error_handler) + { + if (impl != null()) + { + reactor_.close_descriptor(impl); + if (socket_ops::close(impl) == socket_error_retval) + error_handler(boost::asio::error(socket_ops::get_error())); + else + impl = null(); + } + } + + // Bind the socket to the specified local endpoint. + template + void bind(impl_type& impl, const Endpoint& endpoint, + Error_Handler error_handler) + { + if (socket_ops::bind(impl, endpoint.data(), + endpoint.size()) == socket_error_retval) + error_handler(boost::asio::error(socket_ops::get_error())); + } + + // Place the socket into the state where it will listen for new connections. + template + void listen(impl_type& impl, int backlog, Error_Handler error_handler) + { + if (backlog == 0) + backlog = SOMAXCONN; + + if (socket_ops::listen(impl, backlog) == socket_error_retval) + error_handler(boost::asio::error(socket_ops::get_error())); + } + + // Set a socket option. + template + void set_option(impl_type& impl, const Option& option, + Error_Handler error_handler) + { + if (socket_ops::setsockopt(impl, option.level(), option.name(), + option.data(), option.size())) + error_handler(boost::asio::error(socket_ops::get_error())); + } + + // Set a socket option. + template + void get_option(const impl_type& impl, Option& option, + Error_Handler error_handler) const + { + size_t size = option.size(); + if (socket_ops::getsockopt(impl, option.level(), option.name(), + option.data(), &size)) + error_handler(boost::asio::error(socket_ops::get_error())); + } + + // Perform an IO control command on the socket. + template + void io_control(impl_type& impl, IO_Control_Command& command, + Error_Handler error_handler) + { + if (socket_ops::ioctl(impl, command.name(), + static_cast(command.data()))) + error_handler(boost::asio::error(socket_ops::get_error())); + } + + // Get the local endpoint. + template + void get_local_endpoint(const impl_type& impl, Endpoint& endpoint, + Error_Handler error_handler) const + { + socket_addr_len_type addr_len = endpoint.size(); + if (socket_ops::getsockname(impl, endpoint.data(), &addr_len)) + { + error_handler(boost::asio::error(socket_ops::get_error())); + return; + } + + endpoint.size(addr_len); + } + + // Get the remote endpoint. + template + void get_remote_endpoint(const impl_type& impl, Endpoint& endpoint, + Error_Handler error_handler) const + { + socket_addr_len_type addr_len = endpoint.size(); + if (socket_ops::getpeername(impl, endpoint.data(), &addr_len)) + { + error_handler(boost::asio::error(socket_ops::get_error())); + return; + } + + endpoint.size(addr_len); + } + + /// Disable sends or receives on the socket. + template + void shutdown(impl_type& impl, socket_base::shutdown_type what, + Error_Handler error_handler) + { + if (socket_ops::shutdown(impl, what) != 0) + error_handler(boost::asio::error(socket_ops::get_error())); + } + + // Send the given data to the peer. Returns the number of bytes sent or + // 0 if the connection was closed cleanly. + template + size_t send(impl_type& impl, const Const_Buffers& buffers, + socket_base::message_flags flags, Error_Handler error_handler) + { + // Copy buffers into array. + socket_ops::bufs bufs[max_buffers]; + typename Const_Buffers::const_iterator iter = buffers.begin(); + typename Const_Buffers::const_iterator end = buffers.end(); + size_t i = 0; + for (; iter != end && i < max_buffers; ++iter, ++i) + { + bufs[i].size = boost::asio::buffer_size(*iter); + bufs[i].data = const_cast( + boost::asio::buffer_cast(*iter)); + } + + // Send the data. + int bytes_sent = socket_ops::send(impl, bufs, i, flags); + if (bytes_sent < 0) + { + error_handler(boost::asio::error(socket_ops::get_error())); + return 0; + } + return bytes_sent; + } + + template + class send_handler + { + public: + send_handler(impl_type impl, Demuxer& demuxer, const Const_Buffers& buffers, + socket_base::message_flags flags, Handler handler) + : impl_(impl), + demuxer_(demuxer), + work_(demuxer), + buffers_(buffers), + flags_(flags), + handler_(handler) + { + } + + void operator()(int result) + { + // Check whether the operation was successful. + if (result != 0) + { + boost::asio::error error(result); + demuxer_.post(bind_handler(handler_, error, 0)); + return; + } + + // Copy buffers into array. + socket_ops::bufs bufs[max_buffers]; + typename Const_Buffers::const_iterator iter = buffers_.begin(); + typename Const_Buffers::const_iterator end = buffers_.end(); + size_t i = 0; + for (; iter != end && i < max_buffers; ++iter, ++i) + { + bufs[i].size = boost::asio::buffer_size(*iter); + bufs[i].data = const_cast( + boost::asio::buffer_cast(*iter)); + } + + // Send the data. + int bytes = socket_ops::send(impl_, bufs, i, flags_); + boost::asio::error error(bytes < 0 + ? socket_ops::get_error() : boost::asio::error::success); + demuxer_.post(bind_handler(handler_, error, bytes < 0 ? 0 : bytes)); + } + + private: + impl_type impl_; + Demuxer& demuxer_; + typename Demuxer::work work_; + Const_Buffers buffers_; + size_t buffer_count_; + socket_base::message_flags flags_; + Handler handler_; + }; + + // Start an asynchronous send. The data being sent must be valid for the + // lifetime of the asynchronous operation. + template + void async_send(impl_type& impl, const Const_Buffers& buffers, + socket_base::message_flags flags, Handler handler) + { + if (impl == null()) + { + boost::asio::error error(boost::asio::error::bad_descriptor); + demuxer_.post(bind_handler(handler, error, 0)); + } + else + { + reactor_.start_write_op(impl, send_handler( + impl, demuxer_, buffers, flags, handler)); + } + } + + // Send a datagram to the specified endpoint. Returns the number of bytes + // sent. + template + size_t send_to(impl_type& impl, const Const_Buffers& buffers, + socket_base::message_flags flags, const Endpoint& destination, + Error_Handler error_handler) + { + // Copy buffers into array. + socket_ops::bufs bufs[max_buffers]; + typename Const_Buffers::const_iterator iter = buffers.begin(); + typename Const_Buffers::const_iterator end = buffers.end(); + size_t i = 0; + for (; iter != end && i < max_buffers; ++iter, ++i) + { + bufs[i].size = boost::asio::buffer_size(*iter); + bufs[i].data = const_cast( + boost::asio::buffer_cast(*iter)); + } + + // Send the data. + int bytes_sent = socket_ops::sendto(impl, bufs, i, flags, + destination.data(), destination.size()); + if (bytes_sent < 0) + { + error_handler(boost::asio::error(socket_ops::get_error())); + return 0; + } + return bytes_sent; + } + + template + class send_to_handler + { + public: + send_to_handler(impl_type impl, Demuxer& demuxer, + const Const_Buffers& buffers, socket_base::message_flags flags, + const Endpoint& endpoint, Handler handler) + : impl_(impl), + demuxer_(demuxer), + work_(demuxer), + buffers_(buffers), + flags_(flags), + destination_(endpoint), + handler_(handler) + { + } + + void operator()(int result) + { + // Check whether the operation was successful. + if (result != 0) + { + boost::asio::error error(result); + demuxer_.post(bind_handler(handler_, error, 0)); + return; + } + + // Copy buffers into array. + socket_ops::bufs bufs[max_buffers]; + typename Const_Buffers::const_iterator iter = buffers_.begin(); + typename Const_Buffers::const_iterator end = buffers_.end(); + size_t i = 0; + for (; iter != end && i < max_buffers; ++iter, ++i) + { + bufs[i].size = boost::asio::buffer_size(*iter); + bufs[i].data = const_cast( + boost::asio::buffer_cast(*iter)); + } + + // Send the data. + int bytes = socket_ops::sendto(impl_, bufs, i, flags_, + destination_.data(), destination_.size()); + boost::asio::error error(bytes < 0 + ? socket_ops::get_error() : boost::asio::error::success); + demuxer_.post(bind_handler(handler_, error, bytes < 0 ? 0 : bytes)); + } + + private: + impl_type impl_; + Demuxer& demuxer_; + typename Demuxer::work work_; + Const_Buffers buffers_; + socket_base::message_flags flags_; + Endpoint destination_; + Handler handler_; + }; + + // Start an asynchronous send. The data being sent must be valid for the + // lifetime of the asynchronous operation. + template + void async_send_to(impl_type& impl, const Const_Buffers& buffers, + socket_base::message_flags flags, const Endpoint& destination, + Handler handler) + { + if (impl == null()) + { + boost::asio::error error(boost::asio::error::bad_descriptor); + demuxer_.post(bind_handler(handler, error, 0)); + } + else + { + reactor_.start_write_op(impl, + send_to_handler( + impl, demuxer_, buffers, flags, destination, handler)); + } + } + + // Receive some data from the peer. Returns the number of bytes received or + // 0 if the connection was closed cleanly. + template + size_t receive(impl_type& impl, const Mutable_Buffers& buffers, + socket_base::message_flags flags, Error_Handler error_handler) + { + // Copy buffers into array. + socket_ops::bufs bufs[max_buffers]; + typename Mutable_Buffers::const_iterator iter = buffers.begin(); + typename Mutable_Buffers::const_iterator end = buffers.end(); + size_t i = 0; + for (; iter != end && i < max_buffers; ++iter, ++i) + { + bufs[i].size = boost::asio::buffer_size(*iter); + bufs[i].data = boost::asio::buffer_cast(*iter); + } + + // Receive some data. + int bytes_recvd = socket_ops::recv(impl, bufs, i, flags); + if (bytes_recvd < 0) + { + error_handler(boost::asio::error(socket_ops::get_error())); + return 0; + } + if (bytes_recvd == 0) + { + error_handler(boost::asio::error(boost::asio::error::eof)); + return 0; + } + return bytes_recvd; + } + + template + class receive_handler + { + public: + receive_handler(impl_type impl, Demuxer& demuxer, + const Mutable_Buffers& buffers, socket_base::message_flags flags, + Handler handler) + : impl_(impl), + demuxer_(demuxer), + work_(demuxer), + buffers_(buffers), + flags_(flags), + handler_(handler) + { + } + + void operator()(int result) + { + // Check whether the operation was successful. + if (result != 0) + { + boost::asio::error error(result); + demuxer_.post(bind_handler(handler_, error, 0)); + return; + } + + // Copy buffers into array. + socket_ops::bufs bufs[max_buffers]; + typename Mutable_Buffers::const_iterator iter = buffers_.begin(); + typename Mutable_Buffers::const_iterator end = buffers_.end(); + size_t i = 0; + for (; iter != end && i < max_buffers; ++iter, ++i) + { + bufs[i].size = boost::asio::buffer_size(*iter); + bufs[i].data = boost::asio::buffer_cast(*iter); + } + + // Receive some data. + int bytes = socket_ops::recv(impl_, bufs, i, flags_); + int error_code = boost::asio::error::success; + if (bytes < 0) + error_code = socket_ops::get_error(); + else if (bytes == 0) + error_code = boost::asio::error::eof; + boost::asio::error error(error_code); + demuxer_.post(bind_handler(handler_, error, bytes < 0 ? 0 : bytes)); + } + + private: + impl_type impl_; + Demuxer& demuxer_; + typename Demuxer::work work_; + Mutable_Buffers buffers_; + socket_base::message_flags flags_; + Handler handler_; + }; + + // Start an asynchronous receive. The buffer for the data being received + // must be valid for the lifetime of the asynchronous operation. + template + void async_receive(impl_type& impl, const Mutable_Buffers& buffers, + socket_base::message_flags flags, Handler handler) + { + if (impl == null()) + { + boost::asio::error error(boost::asio::error::bad_descriptor); + demuxer_.post(bind_handler(handler, error, 0)); + } + else + { + if (flags & socket_base::message_out_of_band) + { + reactor_.start_except_op(impl, + receive_handler( + impl, demuxer_, buffers, flags, handler)); + } + else + { + reactor_.start_read_op(impl, + receive_handler( + impl, demuxer_, buffers, flags, handler)); + } + } + } + + // Receive a datagram with the endpoint of the sender. Returns the number of + // bytes received. + template + size_t receive_from(impl_type& impl, const Mutable_Buffers& buffers, + socket_base::message_flags flags, Endpoint& sender_endpoint, + Error_Handler error_handler) + { + // Copy buffers into array. + socket_ops::bufs bufs[max_buffers]; + typename Mutable_Buffers::const_iterator iter = buffers.begin(); + typename Mutable_Buffers::const_iterator end = buffers.end(); + size_t i = 0; + for (; iter != end && i < max_buffers; ++iter, ++i) + { + bufs[i].size = boost::asio::buffer_size(*iter); + bufs[i].data = boost::asio::buffer_cast(*iter); + } + + // Receive some data. + socket_addr_len_type addr_len = sender_endpoint.size(); + int bytes_recvd = socket_ops::recvfrom(impl, bufs, i, flags, + sender_endpoint.data(), &addr_len); + if (bytes_recvd < 0) + { + error_handler(boost::asio::error(socket_ops::get_error())); + return 0; + } + if (bytes_recvd == 0) + { + error_handler(boost::asio::error(boost::asio::error::eof)); + return 0; + } + + sender_endpoint.size(addr_len); + + return bytes_recvd; + } + + template + class receive_from_handler + { + public: + receive_from_handler(impl_type impl, Demuxer& demuxer, + const Mutable_Buffers& buffers, socket_base::message_flags flags, + Endpoint& endpoint, Handler handler) + : impl_(impl), + demuxer_(demuxer), + work_(demuxer), + buffers_(buffers), + flags_(flags), + sender_endpoint_(endpoint), + handler_(handler) + { + } + + void operator()(int result) + { + // Check whether the operation was successful. + if (result != 0) + { + boost::asio::error error(result); + demuxer_.post(bind_handler(handler_, error, 0)); + return; + } + + // Copy buffers into array. + socket_ops::bufs bufs[max_buffers]; + typename Mutable_Buffers::const_iterator iter = buffers_.begin(); + typename Mutable_Buffers::const_iterator end = buffers_.end(); + size_t i = 0; + for (; iter != end && i < max_buffers; ++iter, ++i) + { + bufs[i].size = boost::asio::buffer_size(*iter); + bufs[i].data = boost::asio::buffer_cast(*iter); + } + + // Receive some data. + socket_addr_len_type addr_len = sender_endpoint_.size(); + int bytes = socket_ops::recvfrom(impl_, bufs, i, flags_, + sender_endpoint_.data(), &addr_len); + int error_code = boost::asio::error::success; + if (bytes < 0) + error_code = socket_ops::get_error(); + else if (bytes == 0) + error_code = boost::asio::error::eof; + boost::asio::error error(error_code); + sender_endpoint_.size(addr_len); + demuxer_.post(bind_handler(handler_, error, bytes < 0 ? 0 : bytes)); + } + + private: + impl_type impl_; + Demuxer& demuxer_; + typename Demuxer::work work_; + Mutable_Buffers buffers_; + socket_base::message_flags flags_; + Endpoint& sender_endpoint_; + Handler handler_; + }; + + // Start an asynchronous receive. The buffer for the data being received and + // the sender_endpoint object must both be valid for the lifetime of the + // asynchronous operation. + template + void async_receive_from(impl_type& impl, const Mutable_Buffers& buffers, + socket_base::message_flags flags, Endpoint& sender_endpoint, + Handler handler) + { + if (impl == null()) + { + boost::asio::error error(boost::asio::error::bad_descriptor); + demuxer_.post(bind_handler(handler, error, 0)); + } + else + { + reactor_.start_read_op(impl, + receive_from_handler( + impl, demuxer_, buffers, flags, sender_endpoint, handler)); + } + } + + // Accept a new connection. + template + void accept(impl_type& impl, Socket& peer, Error_Handler error_handler) + { + // We cannot accept a socket that is already open. + if (peer.impl() != invalid_socket) + { + error_handler(boost::asio::error(boost::asio::error::already_connected)); + return; + } + + socket_type new_socket = socket_ops::accept(impl, 0, 0); + if (int err = socket_ops::get_error()) + { + error_handler(boost::asio::error(err)); + return; + } + + peer.set_impl(new_socket); + } + + // Accept a new connection. + template + void accept_endpoint(impl_type& impl, Socket& peer, Endpoint& peer_endpoint, + Error_Handler error_handler) + { + // We cannot accept a socket that is already open. + if (peer.impl() != invalid_socket) + { + error_handler(boost::asio::error(boost::asio::error::already_connected)); + return; + } + + socket_addr_len_type addr_len = peer_endpoint.size(); + socket_type new_socket = socket_ops::accept(impl, + peer_endpoint.data(), &addr_len); + if (int err = socket_ops::get_error()) + { + error_handler(boost::asio::error(err)); + return; + } + + peer_endpoint.size(addr_len); + + peer.set_impl(new_socket); + } + + template + class accept_handler + { + public: + accept_handler(impl_type impl, Demuxer& demuxer, Socket& peer, + Handler handler) + : impl_(impl), + demuxer_(demuxer), + work_(demuxer), + peer_(peer), + handler_(handler) + { + } + + void operator()(int result) + { + // Check whether the operation was successful. + if (result != 0) + { + boost::asio::error error(result); + demuxer_.post(bind_handler(handler_, error)); + return; + } + + // Accept the waiting connection. + socket_type new_socket = socket_ops::accept(impl_, 0, 0); + boost::asio::error error(new_socket == invalid_socket + ? socket_ops::get_error() : boost::asio::error::success); + peer_.set_impl(new_socket); + demuxer_.post(bind_handler(handler_, error)); + } + + private: + impl_type impl_; + Demuxer& demuxer_; + typename Demuxer::work work_; + Socket& peer_; + Handler handler_; + }; + + // Start an asynchronous accept. The peer object must be valid until the + // accept's handler is invoked. + template + void async_accept(impl_type& impl, Socket& peer, Handler handler) + { + if (impl == null()) + { + boost::asio::error error(boost::asio::error::bad_descriptor); + demuxer_.post(bind_handler(handler, error)); + } + else if (peer.impl() != invalid_socket) + { + boost::asio::error error(boost::asio::error::already_connected); + demuxer_.post(bind_handler(handler, error)); + } + else + { + reactor_.start_read_op(impl, + accept_handler(impl, demuxer_, peer, handler)); + } + } + + template + class accept_endp_handler + { + public: + accept_endp_handler(impl_type impl, Demuxer& demuxer, Socket& peer, + Endpoint& peer_endpoint, Handler handler) + : impl_(impl), + demuxer_(demuxer), + work_(demuxer), + peer_(peer), + peer_endpoint_(peer_endpoint), + handler_(handler) + { + } + + void operator()(int result) + { + // Check whether the operation was successful. + if (result != 0) + { + boost::asio::error error(result); + demuxer_.post(bind_handler(handler_, error)); + return; + } + + // Accept the waiting connection. + socket_addr_len_type addr_len = peer_endpoint_.size(); + socket_type new_socket = socket_ops::accept(impl_, + peer_endpoint_.data(), &addr_len); + boost::asio::error error(new_socket == invalid_socket + ? socket_ops::get_error() : boost::asio::error::success); + peer_endpoint_.size(addr_len); + peer_.set_impl(new_socket); + demuxer_.post(bind_handler(handler_, error)); + } + + private: + impl_type impl_; + Demuxer& demuxer_; + typename Demuxer::work work_; + Socket& peer_; + Endpoint& peer_endpoint_; + Handler handler_; + }; + + // Start an asynchronous accept. The peer and peer_endpoint objects + // must be valid until the accept's handler is invoked. + template + void async_accept_endpoint(impl_type& impl, Socket& peer, + Endpoint& peer_endpoint, Handler handler) + { + if (impl == null()) + { + boost::asio::error error(boost::asio::error::bad_descriptor); + demuxer_.post(bind_handler(handler, error)); + } + else if (peer.impl() != invalid_socket) + { + boost::asio::error error(boost::asio::error::already_connected); + demuxer_.post(bind_handler(handler, error)); + } + else + { + reactor_.start_read_op(impl, + accept_endp_handler( + impl, demuxer_, peer, peer_endpoint, handler)); + } + } + + // Connect the socket to the specified endpoint. + template + void connect(impl_type& impl, const Endpoint& peer_endpoint, + Error_Handler error_handler) + { + // Open the socket if it is not already open. + if (impl == invalid_socket) + { + // Get the flags used to create the new socket. + int family = peer_endpoint.protocol().family(); + int type = peer_endpoint.protocol().type(); + int proto = peer_endpoint.protocol().protocol(); + + // Create a new socket. + impl = socket_ops::socket(family, type, proto); + if (impl == invalid_socket) + { + error_handler(boost::asio::error(socket_ops::get_error())); + return; + } + } + + // Perform the connect operation. + int result = socket_ops::connect(impl, peer_endpoint.data(), + peer_endpoint.size()); + if (result == socket_error_retval) + error_handler(boost::asio::error(socket_ops::get_error())); + } + + template + class connect_handler + { + public: + connect_handler(impl_type& impl, boost::shared_ptr completed, + Demuxer& demuxer, Reactor& reactor, Handler handler) + : impl_(impl), + completed_(completed), + demuxer_(demuxer), + work_(demuxer), + reactor_(reactor), + handler_(handler) + { + } + + void operator()(int result) + { + // Check whether a handler has already been called for the connection. + // If it has, then we don't want to do anything in this handler. + if (*completed_) + return; + + // Cancel the other reactor operation for the connection. + *completed_ = true; + reactor_.enqueue_cancel_ops_unlocked(impl_); + + // Check whether the operation was successful. + if (result != 0) + { + boost::asio::error error(result); + demuxer_.post(bind_handler(handler_, error)); + return; + } + + // Get the error code from the connect operation. + int connect_error = 0; + size_t connect_error_len = sizeof(connect_error); + if (socket_ops::getsockopt(impl_, SOL_SOCKET, SO_ERROR, + &connect_error, &connect_error_len) == socket_error_retval) + { + boost::asio::error error(socket_ops::get_error()); + demuxer_.post(bind_handler(handler_, error)); + return; + } + + // If connection failed then post the handler with the error code. + if (connect_error) + { + boost::asio::error error(connect_error); + demuxer_.post(bind_handler(handler_, error)); + return; + } + + // Make the socket blocking again (the default). + ioctl_arg_type non_blocking = 0; + if (socket_ops::ioctl(impl_, FIONBIO, &non_blocking)) + { + boost::asio::error error(socket_ops::get_error()); + demuxer_.post(bind_handler(handler_, error)); + return; + } + + // Post the result of the successful connection operation. + boost::asio::error error(boost::asio::error::success); + demuxer_.post(bind_handler(handler_, error)); + } + + private: + impl_type& impl_; + boost::shared_ptr completed_; + Demuxer& demuxer_; + typename Demuxer::work work_; + Reactor& reactor_; + Handler handler_; + }; + + // Start an asynchronous connect. + template + void async_connect(impl_type& impl, const Endpoint& peer_endpoint, + Handler handler) + { + // Open the socket if it is not already open. + if (impl == invalid_socket) + { + // Get the flags used to create the new socket. + int family = peer_endpoint.protocol().family(); + int type = peer_endpoint.protocol().type(); + int proto = peer_endpoint.protocol().protocol(); + + // Create a new socket. + impl = socket_ops::socket(family, type, proto); + if (impl == invalid_socket) + { + boost::asio::error error(socket_ops::get_error()); + demuxer_.post(bind_handler(handler, error)); + return; + } + } + + // Mark the socket as non-blocking so that the connection will take place + // asynchronously. + ioctl_arg_type non_blocking = 1; + if (socket_ops::ioctl(impl, FIONBIO, &non_blocking)) + { + boost::asio::error error(socket_ops::get_error()); + demuxer_.post(bind_handler(handler, error)); + return; + } + + // Start the connect operation. + if (socket_ops::connect(impl, peer_endpoint.data(), + peer_endpoint.size()) == 0) + { + // The connect operation has finished successfully so we need to post the + // handler immediately. + boost::asio::error error(boost::asio::error::success); + demuxer_.post(bind_handler(handler, error)); + } + else if (socket_ops::get_error() == boost::asio::error::in_progress + || socket_ops::get_error() == boost::asio::error::would_block) + { + // The connection is happening in the background, and we need to wait + // until the socket becomes writeable. + boost::shared_ptr completed(new bool(false)); + reactor_.start_write_and_except_ops(impl, connect_handler( + impl, completed, demuxer_, reactor_, handler)); + } + else + { + // The connect operation has failed, so post the handler immediately. + boost::asio::error error(socket_ops::get_error()); + demuxer_.post(bind_handler(handler, error)); + } + } + +private: + // The demuxer used for dispatching handlers. + Demuxer& demuxer_; + + // The selector that performs event demultiplexing for the provider. + Reactor& reactor_; +}; + +} // namespace detail +} // namespace asio +} // namespace boost + +#include + +#endif // BOOST_ASIO_DETAIL_REACTIVE_SOCKET_SERVICE_HPP diff --git a/include/boost/asio/detail/reactor_op_queue.hpp b/include/boost/asio/detail/reactor_op_queue.hpp new file mode 100644 index 00000000..ffb10466 --- /dev/null +++ b/include/boost/asio/detail/reactor_op_queue.hpp @@ -0,0 +1,277 @@ +// +// reactor_op_queue.hpp +// ~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2005 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_DETAIL_REACTOR_OP_QUEUE_HPP +#define BOOST_ASIO_DETAIL_REACTOR_OP_QUEUE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include + +#include +#include +#include + +#include +#include +#include + +namespace boost { +namespace asio { +namespace detail { + +template +class reactor_op_queue + : private noncopyable +{ +public: + // Constructor. + reactor_op_queue() + : operations_(), + cancelled_operations_(0) + { + } + + // Add a new operation to the queue. Returns true if this is the only + // operation for the given descriptor, in which case the reactor's event + // demultiplexing function call may need to be interrupted and restarted. + template + bool enqueue_operation(Descriptor descriptor, Handler handler) + { + op_base* new_op = new op(descriptor, handler); + + typedef typename operation_map::iterator iterator; + typedef typename operation_map::value_type value_type; + std::pair entry = + operations_.insert(value_type(descriptor, new_op)); + if (entry.second) + return true; + + op_base* current_op = entry.first->second; + while (current_op->next_) + current_op = current_op->next_; + current_op->next_ = new_op; + + return false; + } + + // Cancel all operations associated with the descriptor. Any operations + // pending for the descriptor will be notified that they have been cancelled + // next time dispatch_cancellations is called. Returns true if any operations + // were cancelled, in which case the reactor's event demultiplexing function + // may need to be interrupted and restarted. + bool cancel_operations(Descriptor descriptor) + { + typename operation_map::iterator i = operations_.find(descriptor); + if (i != operations_.end()) + { + op_base* last_op = i->second; + while (last_op->next_) + last_op = last_op->next_; + last_op->next_ = cancelled_operations_; + cancelled_operations_ = i->second; + operations_.erase(i); + return true; + } + + return false; + } + + // Whether there are no operations in the queue. + bool empty() const + { + return operations_.empty(); + } + + // Determine whether there are any operations associated with the descriptor. + bool has_operation(Descriptor descriptor) const + { + return operations_.find(descriptor) != operations_.end(); + } + + // Dispatch the first operation corresponding to the descriptor. Returns true + // if there are more operations queued for the descriptor. + bool dispatch_operation(Descriptor descriptor, int result) + { + typename operation_map::iterator i = operations_.find(descriptor); + if (i != operations_.end()) + { + op_base* next_op = i->second->next_; + i->second->next_ = 0; + i->second->invoke(result); + if (next_op) + { + i->second = next_op; + return true; + } + else + { + operations_.erase(i); + return false; + } + } + return false; + } + + // Dispatch all operations corresponding to the descriptor. + void dispatch_all_operations(Descriptor descriptor, int result) + { + typename operation_map::iterator i = operations_.find(descriptor); + if (i != operations_.end()) + { + op_base* op = i->second; + operations_.erase(i); + while (op) + { + op_base* next_op = op->next_; + op->next_ = 0; + op->invoke(result); + op = next_op; + } + } + } + + // Fill a descriptor set with the descriptors corresponding to each active + // operation. + template + void get_descriptors(Descriptor_Set& descriptors) + { + typename operation_map::iterator i = operations_.begin(); + while (i != operations_.end()) + { + descriptors.set(i->first); + ++i; + } + } + + // Dispatch the operations corresponding to the ready file descriptors + // contained in the given descriptor set. + template + void dispatch_descriptors(const Descriptor_Set& descriptors, int result) + { + typename operation_map::iterator i = operations_.begin(); + while (i != operations_.end()) + { + typename operation_map::iterator op = i++; + if (descriptors.is_set(op->first)) + { + op_base* next_op = op->second->next_; + op->second->next_ = 0; + op->second->invoke(result); + if (next_op) + op->second = next_op; + else + operations_.erase(op); + } + } + } + + // Dispatch any pending cancels for operations. + void dispatch_cancellations() + { + while (cancelled_operations_) + { + op_base* next_op = cancelled_operations_->next_; + cancelled_operations_->next_ = 0; + cancelled_operations_->invoke(boost::asio::error::operation_aborted); + cancelled_operations_ = next_op; + } + } + +private: + // Base class for reactor operations. A function pointer is used instead of + // virtual functions to avoid the associated overhead. + class op_base + { + public: + // Get the descriptor associated with the operation. + Descriptor descriptor() const + { + return descriptor_; + } + + // Perform the operation. + void invoke(int result) + { + func_(this, result); + } + + protected: + typedef void (*func_type)(op_base*, int); + + // Construct an operation for the given descriptor. + op_base(func_type func, Descriptor descriptor) + : func_(func), + descriptor_(descriptor), + next_(0) + { + } + + // Prevent deletion through this type. + ~op_base() + { + } + + private: + friend class reactor_op_queue; + + // The function to be called to dispatch the handler. + func_type func_; + + // The descriptor associated with the operation. + Descriptor descriptor_; + + // The next operation for the same file descriptor. + op_base* next_; + }; + + // Adaptor class template for using handlers in operations. + template + class op + : public op_base + { + public: + // Constructor. + op(Descriptor descriptor, Handler handler) + : op_base(&op::invoke_handler, descriptor), + handler_(handler) + { + } + + // Invoke the handler. + static void invoke_handler(op_base* base, int result) + { + std::auto_ptr > o(static_cast*>(base)); + o->handler_(result); + } + + private: + Handler handler_; + }; + + // The type for a map of operations. + typedef hash_map operation_map; + + // The operations that are currently executing asynchronously. + operation_map operations_; + + // The list of operations that have been cancelled. + op_base* cancelled_operations_; +}; + +} // namespace detail +} // namespace asio +} // namespace boost + +#include + +#endif // BOOST_ASIO_DETAIL_REACTOR_OP_QUEUE_HPP diff --git a/include/boost/asio/detail/reactor_timer_queue.hpp b/include/boost/asio/detail/reactor_timer_queue.hpp new file mode 100644 index 00000000..fd7f3850 --- /dev/null +++ b/include/boost/asio/detail/reactor_timer_queue.hpp @@ -0,0 +1,305 @@ +// +// reactor_timer_queue.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2005 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_DETAIL_REACTOR_TIMER_QUEUE_HPP +#define BOOST_ASIO_DETAIL_REACTOR_TIMER_QUEUE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace boost { +namespace asio { +namespace detail { + +template > +class reactor_timer_queue + : private noncopyable +{ +public: + // Constructor. + reactor_timer_queue() + : timers_(), + heap_() + { + } + + // Add a new timer to the queue. Returns true if this is the timer that is + // earliest in the queue, in which case the reactor's event demultiplexing + // function call may need to be interrupted and restarted. + template + bool enqueue_timer(const Time& time, Handler handler, void* token) + { + // Ensure that there is space for the timer in the heap. We reserve here so + // that the push_back below will not throw due to a reallocation failure. + heap_.reserve(heap_.size() + 1); + + // Create a new timer object. + std::auto_ptr > new_timer( + new timer(time, handler, token)); + + // Insert the new timer into the hash. + typedef typename hash_map::iterator iterator; + typedef typename hash_map::value_type value_type; + std::pair result = + timers_.insert(value_type(token, new_timer.get())); + if (!result.second) + { + result.first->second->prev_ = new_timer.get(); + new_timer->next_ = result.first->second; + result.first->second = new_timer.get(); + } + + // Put the timer at the correct position in the heap. + new_timer->heap_index_ = heap_.size(); + heap_.push_back(new_timer.get()); + up_heap(heap_.size() - 1); + bool is_first = (heap_[0] == new_timer.get()); + + // Ownership of the timer is transferred to the timer queue. + new_timer.release(); + + return is_first; + } + + // Whether there are no timers in the queue. + bool empty() const + { + return heap_.empty(); + } + + // Get the time for the timer that is earliest in the queue. + void get_earliest_time(Time& time) + { + time = heap_[0]->time_; + } + + // Dispatch the timers that are earlier than the specified time. + void dispatch_timers(const Time& time) + { + Comparator comp; + while (!heap_.empty() && comp(heap_[0]->time_, time)) + { + timer_base* t = heap_[0]; + remove_timer(t); + t->invoke(0); + } + } + + // Cancel the timer with the given token. The handler will be invoked + // immediately with the result operation_aborted. + std::size_t cancel_timer(void* timer_token) + { + std::size_t num_cancelled = 0; + typedef typename hash_map::iterator iterator; + iterator it = timers_.find(timer_token); + if (it != timers_.end()) + { + timer_base* t = it->second; + while (t) + { + timer_base* next = t->next_; + remove_timer(t); + t->invoke(boost::asio::error::operation_aborted); + t = next; + ++num_cancelled; + } + } + return num_cancelled; + } + +private: + // Base class for timer operations. A function pointer is used instead of + // virtual functions to avoid the associated overhead. + class timer_base + { + public: + // Perform the timer operation. + void invoke(int result) + { + func_(this, result); + } + + protected: + typedef void (*func_type)(timer_base*, int); + + // Constructor. + timer_base(func_type func, const Time& time, void* token) + : func_(func), + time_(time), + token_(token), + next_(0), + prev_(0), + heap_index_( + std::numeric_limits::max BOOST_PREVENT_MACRO_SUBSTITUTION()) + { + } + + // Prevent deletion through this type. + ~timer_base() + { + } + + private: + friend class reactor_timer_queue; + + // The function to be called to dispatch the handler. + func_type func_; + + // The time when the operation should fire. + Time time_; + + // The token associated with the timer. + void* token_; + + // The next timer known to the queue. + timer_base* next_; + + // The previous timer known to the queue. + timer_base* prev_; + + // The index of the timer in the heap. + size_t heap_index_; + }; + + // Adaptor class template for using handlers in timers. + template + class timer + : public timer_base + { + public: + // Constructor. + timer(const Time& time, Handler handler, void* token) + : timer_base(&timer::invoke_handler, time, token), + handler_(handler) + { + } + + // Invoke the handler. + static void invoke_handler(timer_base* base, int result) + { + std::auto_ptr > t(static_cast*>(base)); + t->handler_(result); + } + + private: + Handler handler_; + }; + + // Move the item at the given index up the heap to its correct position. + void up_heap(size_t index) + { + Comparator comp; + size_t parent = (index - 1) / 2; + while (index > 0 && comp(heap_[index]->time_, heap_[parent]->time_)) + { + swap_heap(index, parent); + index = parent; + parent = (index - 1) / 2; + } + } + + // Move the item at the given index down the heap to its correct position. + void down_heap(size_t index) + { + Comparator comp; + size_t child = index * 2 + 1; + while (child < heap_.size()) + { + size_t min_child = (child + 1 == heap_.size() + || comp(heap_[child]->time_, heap_[child + 1]->time_)) + ? child : child + 1; + if (comp(heap_[index]->time_, heap_[min_child]->time_)) + break; + swap_heap(index, min_child); + index = min_child; + child = index * 2 + 1; + } + } + + // Swap two entries in the heap. + void swap_heap(size_t index1, size_t index2) + { + timer_base* tmp = heap_[index1]; + heap_[index1] = heap_[index2]; + heap_[index2] = tmp; + heap_[index1]->heap_index_ = index1; + heap_[index2]->heap_index_ = index2; + } + + // Remove a timer from the heap and list of timers. + void remove_timer(timer_base* t) + { + // Remove the timer from the heap. + size_t index = t->heap_index_; + if (!heap_.empty() && index < heap_.size()) + { + if (index == heap_.size() - 1) + { + heap_.pop_back(); + } + else + { + swap_heap(index, heap_.size() - 1); + heap_.pop_back(); + Comparator comp; + size_t parent = (index - 1) / 2; + if (index > 0 && comp(t->time_, heap_[parent]->time_)) + up_heap(index); + else + down_heap(index); + } + } + + // Remove the timer from the hash. + typedef typename hash_map::iterator iterator; + iterator it = timers_.find(t->token_); + if (it != timers_.end()) + { + if (it->second == t) + it->second = t->next_; + if (t->prev_) + t->prev_->next_ = t->next_; + if (t->next_) + t->next_->prev_ = t->prev_; + if (it->second == 0) + timers_.erase(it); + } + } + + // A hash of timer token to linked lists of timers. + hash_map timers_; + + // The heap of timers, with the earliest timer at the front. + std::vector heap_; +}; + +} // namespace detail +} // namespace asio +} // namespace boost + +#include + +#endif // BOOST_ASIO_DETAIL_REACTOR_TIMER_QUEUE_HPP diff --git a/include/boost/asio/detail/scoped_lock.hpp b/include/boost/asio/detail/scoped_lock.hpp new file mode 100644 index 00000000..4908ded9 --- /dev/null +++ b/include/boost/asio/detail/scoped_lock.hpp @@ -0,0 +1,81 @@ +// +// scoped_lock.hpp +// ~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2005 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_DETAIL_SCOPED_LOCK_HPP +#define BOOST_ASIO_DETAIL_SCOPED_LOCK_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include + +#include + +namespace boost { +namespace asio { +namespace detail { + +// Helper class to lock and unlock a mutex automatically. +template +class scoped_lock + : private noncopyable +{ +public: + // Constructor acquires the lock. + scoped_lock(Mutex& m) + : mutex_(m) + { + mutex_.lock(); + locked_ = true; + } + + // Destructor releases the lock. + ~scoped_lock() + { + if (locked_) + mutex_.unlock(); + } + + // Explicitly acquire the lock. + void lock() + { + if (!locked_) + { + mutex_.lock(); + locked_ = true; + } + } + + // Explicitly release the lock. + void unlock() + { + if (locked_) + { + mutex_.unlock(); + locked_ = false; + } + } + +private: + // The underlying mutex. + Mutex& mutex_; + + // Whether the mutex is currently locked or unlocked. + bool locked_; +}; + +} // namespace detail +} // namespace asio +} // namespace boost + +#include + +#endif // BOOST_ASIO_DETAIL_SCOPED_LOCK_HPP diff --git a/include/boost/asio/detail/select_interrupter.hpp b/include/boost/asio/detail/select_interrupter.hpp new file mode 100644 index 00000000..92081d45 --- /dev/null +++ b/include/boost/asio/detail/select_interrupter.hpp @@ -0,0 +1,43 @@ +// +// select_interrupter.hpp +// ~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2005 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_DETAIL_SELECT_INTERRUPTER_HPP +#define BOOST_ASIO_DETAIL_SELECT_INTERRUPTER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include + +#include +#include +#include + +#include +#include + +namespace boost { +namespace asio { +namespace detail { + +#if defined(BOOST_WINDOWS) +typedef socket_select_interrupter select_interrupter; +#else +typedef pipe_select_interrupter select_interrupter; +#endif + +} // namespace detail +} // namespace asio +} // namespace boost + +#include + +#endif // BOOST_ASIO_DETAIL_SELECT_INTERRUPTER_HPP diff --git a/include/boost/asio/detail/select_reactor.hpp b/include/boost/asio/detail/select_reactor.hpp new file mode 100644 index 00000000..b34702d6 --- /dev/null +++ b/include/boost/asio/detail/select_reactor.hpp @@ -0,0 +1,358 @@ +// +// select_reactor.hpp +// ~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2005 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_DETAIL_SELECT_REACTOR_HPP +#define BOOST_ASIO_DETAIL_SELECT_REACTOR_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace boost { +namespace asio { +namespace detail { + +template +class select_reactor + : private noncopyable +{ +public: + // Constructor. + template + select_reactor(Demuxer&) + : mutex_(), + select_in_progress_(false), + interrupter_(), + read_op_queue_(), + write_op_queue_(), + except_op_queue_(), + pending_cancellations_(), + stop_thread_(false), + thread_(0) + { + if (Own_Thread) + { + boost::asio::detail::signal_blocker sb; + thread_ = new boost::asio::detail::thread( + bind_handler(&select_reactor::call_run_thread, this)); + } + } + + // Destructor. + ~select_reactor() + { + if (thread_) + { + boost::asio::detail::mutex::scoped_lock lock(mutex_); + stop_thread_ = true; + lock.unlock(); + interrupter_.interrupt(); + thread_->join(); + delete thread_; + } + } + + // Start a new read operation. The handler object will be invoked when the + // given descriptor is ready to be read, or an error has occurred. + template + void start_read_op(socket_type descriptor, Handler handler) + { + boost::asio::detail::mutex::scoped_lock lock(mutex_); + if (read_op_queue_.enqueue_operation(descriptor, handler)) + interrupter_.interrupt(); + } + + // Start a new write operation. The handler object will be invoked when the + // given descriptor is ready to be written, or an error has occurred. + template + void start_write_op(socket_type descriptor, Handler handler) + { + boost::asio::detail::mutex::scoped_lock lock(mutex_); + if (write_op_queue_.enqueue_operation(descriptor, handler)) + interrupter_.interrupt(); + } + + // Start a new exception operation. The handler object will be invoked when + // the given descriptor has exception information, or an error has occurred. + template + void start_except_op(socket_type descriptor, Handler handler) + { + boost::asio::detail::mutex::scoped_lock lock(mutex_); + if (except_op_queue_.enqueue_operation(descriptor, handler)) + interrupter_.interrupt(); + } + + // Start new write and exception operations. The handler object will be + // invoked when the given descriptor is ready for writing or has exception + // information available, or an error has occurred. + template + void start_write_and_except_ops(socket_type descriptor, Handler handler) + { + boost::asio::detail::mutex::scoped_lock lock(mutex_); + bool interrupt = write_op_queue_.enqueue_operation(descriptor, handler); + interrupt = except_op_queue_.enqueue_operation(descriptor, handler) + || interrupt; + if (interrupt) + interrupter_.interrupt(); + } + + // Cancel all operations associated with the given descriptor. The + // handlers associated with the descriptor will be invoked with the + // operation_aborted error. + void cancel_ops(socket_type descriptor) + { + boost::asio::detail::mutex::scoped_lock lock(mutex_); + cancel_ops_unlocked(descriptor); + } + + // Enqueue cancellation of all operations associated with the given + // descriptor. The handlers associated with the descriptor will be invoked + // with the operation_aborted error. This function does not acquire the + // select_reactor's mutex, and so should only be used from within a reactor + // handler. + void enqueue_cancel_ops_unlocked(socket_type descriptor) + { + pending_cancellations_.insert( + pending_cancellations_map::value_type(descriptor, true)); + } + + // Cancel any operations that are running against the descriptor and remove + // its registration from the reactor. + void close_descriptor(socket_type descriptor) + { + boost::asio::detail::mutex::scoped_lock lock(mutex_); + cancel_ops_unlocked(descriptor); + } + + // Schedule a timer to expire at the specified absolute time. The handler + // object will be invoked when the timer expires. + template + void schedule_timer(const boost::posix_time::ptime& time, + Handler handler, void* token) + { + boost::asio::detail::mutex::scoped_lock lock(mutex_); + if (timer_queue_.enqueue_timer(time, handler, token)) + interrupter_.interrupt(); + } + + // Cancel the timer associated with the given token. Returns the number of + // handlers that have been posted or dispatched. + std::size_t cancel_timer(void* token) + { + boost::asio::detail::mutex::scoped_lock lock(mutex_); + return timer_queue_.cancel_timer(token); + } + +private: + friend class task_demuxer_service >; + + // Reset the select loop before a new run. + void reset() + { + boost::asio::detail::mutex::scoped_lock lock(mutex_); + stop_thread_ = false; + interrupter_.reset(); + } + + // Run the select loop. + void run() + { + boost::asio::detail::mutex::scoped_lock lock(mutex_); + + // Dispatch any operation cancellations that were made while the select + // loop was not running. + read_op_queue_.dispatch_cancellations(); + write_op_queue_.dispatch_cancellations(); + except_op_queue_.dispatch_cancellations(); + + bool stop = false; + while (!stop && !stop_thread_) + { + // Set up the descriptor sets. + fd_set_adapter read_fds; + read_fds.set(interrupter_.read_descriptor()); + read_op_queue_.get_descriptors(read_fds); + fd_set_adapter write_fds; + write_op_queue_.get_descriptors(write_fds); + fd_set_adapter except_fds; + except_op_queue_.get_descriptors(except_fds); + socket_type max_fd = read_fds.max_descriptor(); + if (write_fds.max_descriptor() > max_fd) + max_fd = write_fds.max_descriptor(); + if (except_fds.max_descriptor() > max_fd) + max_fd = except_fds.max_descriptor(); + + // Block on the select call without holding the lock so that new + // operations can be started while the call is executing. + timeval tv_buf; + timeval* tv = get_timeout(tv_buf); + select_in_progress_ = true; + lock.unlock(); + int retval = socket_ops::select(static_cast(max_fd + 1), + read_fds, write_fds, except_fds, tv); + lock.lock(); + select_in_progress_ = false; + + // Block signals while dispatching operations. + boost::asio::detail::signal_blocker sb; + + // Reset the interrupter. + if (retval > 0 && read_fds.is_set(interrupter_.read_descriptor())) + stop = interrupter_.reset(); + + // Dispatch all ready operations. + if (retval > 0) + { + // Exception operations must be processed first to ensure that any + // out-of-band data is read before normal data. + except_op_queue_.dispatch_descriptors(except_fds, 0); + read_op_queue_.dispatch_descriptors(read_fds, 0); + write_op_queue_.dispatch_descriptors(write_fds, 0); + except_op_queue_.dispatch_cancellations(); + read_op_queue_.dispatch_cancellations(); + write_op_queue_.dispatch_cancellations(); + } + timer_queue_.dispatch_timers( + boost::posix_time::microsec_clock::universal_time()); + + // Issue any pending cancellations. + pending_cancellations_map::iterator i = pending_cancellations_.begin(); + while (i != pending_cancellations_.end()) + { + cancel_ops_unlocked(i->first); + ++i; + } + pending_cancellations_.clear(); + } + } + + // Run the select loop in the thread. + void run_thread() + { + boost::asio::detail::mutex::scoped_lock lock(mutex_); + while (!stop_thread_) + { + lock.unlock(); + run(); + lock.lock(); + } + } + + // Entry point for the select loop thread. + static void call_run_thread(select_reactor* reactor) + { + reactor->run_thread(); + } + + // Interrupt the select loop. + void interrupt() + { + interrupter_.interrupt(); + } + + // Get the timeout value for the select call. + timeval* get_timeout(timeval& tv) + { + if (timer_queue_.empty()) + return 0; + + boost::posix_time::ptime now + = boost::posix_time::microsec_clock::universal_time(); + boost::posix_time::ptime earliest_timer; + timer_queue_.get_earliest_time(earliest_timer); + if (now < earliest_timer) + { + boost::posix_time::time_duration timeout = earliest_timer - now; + tv.tv_sec = timeout.total_seconds(); + tv.tv_usec = timeout.total_microseconds() % 1000000; + } + else + { + tv.tv_sec = 0; + tv.tv_usec = 0; + } + + return &tv; + } + + // Cancel all operations associated with the given descriptor. The do_cancel + // function of the handler objects will be invoked. This function does not + // acquire the select_reactor's mutex. + void cancel_ops_unlocked(socket_type descriptor) + { + bool interrupt = read_op_queue_.cancel_operations(descriptor); + interrupt = write_op_queue_.cancel_operations(descriptor) || interrupt; + interrupt = except_op_queue_.cancel_operations(descriptor) || interrupt; + if (interrupt) + interrupter_.interrupt(); + } + + // Mutex to protect access to internal data. + boost::asio::detail::mutex mutex_; + + // Whether the select loop is currently running or not. + bool select_in_progress_; + + // The interrupter is used to break a blocking select call. + select_interrupter interrupter_; + + // The queue of read operations. + reactor_op_queue read_op_queue_; + + // The queue of write operations. + reactor_op_queue write_op_queue_; + + // The queue of exception operations. + reactor_op_queue except_op_queue_; + + // The queue of timers. + reactor_timer_queue timer_queue_; + + // The type for a map of descriptors to be cancelled. + typedef hash_map pending_cancellations_map; + + // The map of descriptors that are pending cancellation. + pending_cancellations_map pending_cancellations_; + + // Does the reactor loop thread need to stop. + bool stop_thread_; + + // The thread that is running the reactor loop. + boost::asio::detail::thread* thread_; +}; + +} // namespace detail +} // namespace asio +} // namespace boost + +#include + +#endif // BOOST_ASIO_DETAIL_SELECT_REACTOR_HPP diff --git a/include/boost/asio/detail/service_registry.hpp b/include/boost/asio/detail/service_registry.hpp new file mode 100644 index 00000000..23cd5e95 --- /dev/null +++ b/include/boost/asio/detail/service_registry.hpp @@ -0,0 +1,168 @@ +// +// service_registry.hpp +// ~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2005 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_DETAIL_SERVICE_REGISTRY_HPP +#define BOOST_ASIO_DETAIL_SERVICE_REGISTRY_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include + +#include +#include +#include +#include + +#include +#include +#include + +namespace boost { +namespace asio { +namespace detail { + +template +class service_registry + : private noncopyable +{ +public: + // Constructor. + service_registry(Owner& o) + : owner_(o), + first_service_(0) + { + } + + // Destructor. + ~service_registry() + { + while (first_service_) + { + service_holder_base* next_service = first_service_->next_; + delete first_service_; + first_service_ = next_service; + } + } + + // Get the service object corresponding to the specified service type. Will + // create a new service object automatically if no such object already + // exists. Ownership of the service object is not transferred to the caller. + template + Service& get_service(service_factory factory) + { + boost::asio::detail::mutex::scoped_lock lock(mutex_); + + // First see if there is an existing service object for the given type. + service_holder_base* service = first_service_; + while (service) + { + if (service->is_same_type(typeid(Service))) + { + service_holder* typed_service = + static_cast*>(service); + return typed_service->service(); + } + service = service->next_; + } + + // Create a new service object. The service registry's mutex is not locked + // at this time to allow for nested calls into this function from the new + // service's constructor. + lock.unlock(); + std::auto_ptr > new_service( + new service_holder(factory, owner_)); + Service& new_service_ref = new_service->service(); + lock.lock(); + + // Service was successfully initialised, pass ownership to registry. + new_service->next_ = first_service_; + first_service_ = new_service.release(); + + return new_service_ref; + } + +private: + // The base holder for a single service. + class service_holder_base + : private noncopyable + { + public: + // Constructor. + service_holder_base() + : next_(0) + { + } + + // Destructor. + virtual ~service_holder_base() + { + } + + // Determine whether this service is the given type. + virtual bool is_same_type(const std::type_info&) = 0; + + // A pointer to the next service holder in the list. + service_holder_base* next_; + }; + + // Template used as the concrete holder for the service types. + template + class service_holder + : public service_holder_base + { + public: + // Constructor. + service_holder(service_factory& factory, Owner& owner) + : service_(factory.create(owner)) + { + } + + // Destructor. + virtual ~service_holder() + { + delete service_; + } + + // Determine whether this service is the given type. + virtual bool is_same_type(const std::type_info& other_info) + { + return other_info == typeid(Service) ? true : false; + } + + // Get a pointer to the contained service. + Service& service() + { + return *service_; + } + + private: + // The contained service object. + Service* service_; + }; + + // Mutex to protect access to internal data. + boost::asio::detail::mutex mutex_; + + // The owner of this service registry and the services it contains. + Owner& owner_; + + // The first service in the list of contained services. + service_holder_base* first_service_; +}; + +} // namespace detail +} // namespace asio +} // namespace boost + +#include + +#endif // BOOST_ASIO_DETAIL_SERVICE_REGISTRY_HPP diff --git a/include/boost/asio/detail/signal_blocker.hpp b/include/boost/asio/detail/signal_blocker.hpp new file mode 100644 index 00000000..5d679ac0 --- /dev/null +++ b/include/boost/asio/detail/signal_blocker.hpp @@ -0,0 +1,51 @@ +// +// signal_blocker.hpp +// ~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2005 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_DETAIL_SIGNAL_BLOCKER_HPP +#define BOOST_ASIO_DETAIL_SIGNAL_BLOCKER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include + +#include +#include +#include + +#include +#include + +#if defined(BOOST_WINDOWS) +# include +#elif defined(BOOST_HAS_PTHREADS) +# include +#else +# error Only Windows and POSIX are supported! +#endif + +namespace boost { +namespace asio { +namespace detail { + +#if defined(BOOST_WINDOWS) +typedef win_signal_blocker signal_blocker; +#elif defined(BOOST_HAS_PTHREADS) +typedef posix_signal_blocker signal_blocker; +#endif + +} // namespace detail +} // namespace asio +} // namespace boost + +#include + +#endif // BOOST_ASIO_DETAIL_SIGNAL_BLOCKER_HPP diff --git a/include/boost/asio/detail/signal_init.hpp b/include/boost/asio/detail/signal_init.hpp new file mode 100644 index 00000000..a1a75cb1 --- /dev/null +++ b/include/boost/asio/detail/signal_init.hpp @@ -0,0 +1,53 @@ +// +// signal_init.hpp +// ~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2005 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_DETAIL_SIGNAL_INIT_HPP +#define BOOST_ASIO_DETAIL_SIGNAL_INIT_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include + +#include +#include +#include + +#if !defined(BOOST_WINDOWS) + +#include +#include +#include + +namespace boost { +namespace asio { +namespace detail { + +template +class signal_init +{ +public: + // Constructor. + signal_init() + { + std::signal(Signal, SIG_IGN); + } +}; + +} // namespace detail +} // namespace asio +} // namespace boost + +#endif // !defined(BOOST_WINDOWS) + +#include + +#endif // BOOST_ASIO_DETAIL_SIGNAL_INIT_HPP diff --git a/include/boost/asio/detail/socket_holder.hpp b/include/boost/asio/detail/socket_holder.hpp new file mode 100644 index 00000000..0d3e04b8 --- /dev/null +++ b/include/boost/asio/detail/socket_holder.hpp @@ -0,0 +1,93 @@ +// +// socket_holder.hpp +// ~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2005 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_DETAIL_SOCKET_HOLDER_HPP +#define BOOST_ASIO_DETAIL_SOCKET_HOLDER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include + +#include +#include + +namespace boost { +namespace asio { +namespace detail { + +// Implement the resource acquisition is initialisation idiom for sockets. +class socket_holder + : private noncopyable +{ +public: + // Construct as an uninitialised socket. + socket_holder() + : socket_(invalid_socket) + { + } + + // Construct to take ownership of the specified socket. + explicit socket_holder(socket_type s) + : socket_(s) + { + } + + // Destructor. + ~socket_holder() + { + if (socket_ != invalid_socket) + socket_ops::close(socket_); + } + + // Get the underlying socket. + socket_type get() const + { + return socket_; + } + + // Reset to an uninitialised socket. + void reset() + { + if (socket_ != invalid_socket) + { + socket_ops::close(socket_); + socket_ = invalid_socket; + } + } + + // Reset to take ownership of the specified socket. + void reset(socket_type s) + { + reset(); + socket_ = s; + } + + // Release ownership of the socket. + socket_type release() + { + socket_type tmp = socket_; + socket_ = invalid_socket; + return tmp; + } + +private: + // The underlying socket. + socket_type socket_; +}; + +} // namespace detail +} // namespace asio +} // namespace boost + +#include + +#endif // BOOST_ASIO_DETAIL_SOCKET_HOLDER_HPP diff --git a/include/boost/asio/detail/socket_ops.hpp b/include/boost/asio/detail/socket_ops.hpp new file mode 100644 index 00000000..aed3e022 --- /dev/null +++ b/include/boost/asio/detail/socket_ops.hpp @@ -0,0 +1,669 @@ +// +// socket_ops.hpp +// ~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2005 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_DETAIL_SOCKET_OPS_HPP +#define BOOST_ASIO_DETAIL_SOCKET_OPS_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace boost { +namespace asio { +namespace detail { +namespace socket_ops { + +inline int get_error() +{ +#if defined(BOOST_WINDOWS) + return WSAGetLastError(); +#else // defined(BOOST_WINDOWS) + return errno; +#endif // defined(BOOST_WINDOWS) +} + +inline void set_error(int error) +{ + errno = error; +#if defined(BOOST_WINDOWS) + WSASetLastError(error); +#endif // defined(BOOST_WINDOWS) +} + +template +inline ReturnType error_wrapper(ReturnType return_value) +{ +#if defined(BOOST_WINDOWS) + errno = WSAGetLastError(); +#endif // defined(BOOST_WINDOWS) + return return_value; +} + +inline socket_type accept(socket_type s, socket_addr_type* addr, + socket_addr_len_type* addrlen) +{ + set_error(0); + return error_wrapper(::accept(s, addr, addrlen)); +} + +inline int bind(socket_type s, const socket_addr_type* addr, + socket_addr_len_type addrlen) +{ + set_error(0); + return error_wrapper(::bind(s, addr, addrlen)); +} + +inline int close(socket_type s) +{ + set_error(0); +#if defined(BOOST_WINDOWS) + return error_wrapper(::closesocket(s)); +#else // defined(BOOST_WINDOWS) + return error_wrapper(::close(s)); +#endif // defined(BOOST_WINDOWS) +} + +inline int shutdown(socket_type s, int what) +{ + set_error(0); + return error_wrapper(::shutdown(s, what)); +} + +inline int connect(socket_type s, const socket_addr_type* addr, + socket_addr_len_type addrlen) +{ + set_error(0); + return error_wrapper(::connect(s, addr, addrlen)); +} + +inline int listen(socket_type s, int backlog) +{ + set_error(0); + return error_wrapper(::listen(s, backlog)); +} + +struct bufs +{ + void* data; + size_t size; +}; + +enum { max_bufs = 16 }; + +inline int recv(socket_type s, bufs* b, size_t count, int flags) +{ + set_error(0); +#if defined(BOOST_WINDOWS) + // Copy buffers into WSABUF array. + WSABUF recv_bufs[max_bufs]; + if (count > max_bufs) + count = max_bufs; + for (size_t i = 0; i < count; ++i) + { + recv_bufs[i].len = static_cast(b[i].size); + recv_bufs[i].buf = static_cast(b[i].data); + } + + // Receive some data. + DWORD recv_buf_count = static_cast(count); + DWORD bytes_transferred = 0; + DWORD recv_flags = flags; + int result = error_wrapper(::WSARecv(s, recv_bufs, + recv_buf_count, &bytes_transferred, &recv_flags, 0, 0)); + if (result != 0) + return -1; + return bytes_transferred; +#else // defined(BOOST_WINDOWS) + if (count == 1) + { + // We only have to receive into a single buffer, so use normal recv call. + return error_wrapper(::recv(s, b[0].data, b[0].size, flags)); + } + else if (flags == 0) + { + // No flags have been specified so we can perform this receive operation + // using a vectored-read function, i.e. readv. + + // Copy buffers into iovec array. + iovec recv_bufs[max_bufs]; + if (count > max_bufs) + count = max_bufs; + for (size_t i = 0; i < count; ++i) + { + recv_bufs[i].iov_len = b[i].size; + recv_bufs[i].iov_base = b[i].data; + } + + // Receive some data. + return error_wrapper(::readv(s, recv_bufs, count)); + } + else + { + // Socket flags have been supplied so receive into a temporary buffer and + // then copy the data to the caller-supplied buffers. + + // Create a buffer of the appropriate size. + size_t buf_size = 0; + for (size_t i = 0; i < count; ++i) + buf_size += b[i].size; + std::vector buffer(buf_size); + + // Receive some data. + int result = error_wrapper(::recv(s, &buffer[0], buf_size, flags)); + if (result <= 0) + return result; + + // Copy to caller-supplied buffers. + using namespace std; // For memcpy. + size_t bytes_avail = result; + size_t bytes_copied = 0; + for (size_t i = 0; i < count && bytes_avail > 0; ++i) + { + size_t size = (b[i].size < bytes_avail) ? b[i].size : bytes_avail; + memcpy(b[i].data, &buffer[bytes_copied], size); + bytes_copied += size; + bytes_avail -= size; + } + + return result; + } +#endif // defined(BOOST_WINDOWS) +} + +inline int recvfrom(socket_type s, bufs* b, size_t count, int flags, + socket_addr_type* addr, socket_addr_len_type* addrlen) +{ + set_error(0); +#if defined(BOOST_WINDOWS) + // Copy buffers into WSABUF array. + WSABUF recv_bufs[max_bufs]; + if (count > max_bufs) + count = max_bufs; + for (size_t i = 0; i < count; ++i) + { + recv_bufs[i].len = static_cast(b[i].size); + recv_bufs[i].buf = static_cast(b[i].data); + } + + // Receive some data. + DWORD recv_buf_count = static_cast(count); + DWORD bytes_transferred = 0; + DWORD recv_flags = flags; + int result = error_wrapper(::WSARecvFrom(s, recv_bufs, recv_buf_count, + &bytes_transferred, &recv_flags, addr, addrlen, 0, 0)); + if (result != 0) + return -1; + return bytes_transferred; +#else // defined(BOOST_WINDOWS) + if (count == 1) + { + // We only have to receive into a single buffer, so use normal recvfrom + // call. + return error_wrapper(::recvfrom(s, + b[0].data, b[0].size, flags, addr, addrlen)); + } + else + { + // We have to receive into multiple buffers, so receive into a temporary + // buffer and then copy the data to the caller-supplied buffers. + + // Create a buffer of the appropriate size. + size_t buf_size = 0; + for (size_t i = 0; i < count; ++i) + buf_size += b[i].size; + std::vector buffer(buf_size); + + // Receive some data. + int result = error_wrapper(::recvfrom(s, + &buffer[0], buf_size, flags, addr, addrlen)); + if (result <= 0) + return result; + + // Copy to caller-supplied buffers. + using namespace std; // For memcpy. + size_t bytes_avail = result; + size_t bytes_copied = 0; + for (size_t i = 0; i < count && bytes_avail > 0; ++i) + { + size_t size = (b[i].size < bytes_avail) ? b[i].size : bytes_avail; + memcpy(b[i].data, &buffer[bytes_copied], size); + bytes_copied += size; + bytes_avail -= size; + } + + return result; + } +#endif // defined(BOOST_WINDOWS) +} + +inline int send(socket_type s, const bufs* b, size_t count, int flags) +{ + set_error(0); +#if defined(BOOST_WINDOWS) + // Copy buffers into WSABUF array. + WSABUF send_bufs[max_bufs]; + if (count > max_bufs) + count = max_bufs; + for (size_t i = 0; i < count; ++i) + { + send_bufs[i].len = static_cast(b[i].size); + send_bufs[i].buf = static_cast(b[i].data); + } + + // Send the data. + DWORD send_buf_count = static_cast(count); + DWORD bytes_transferred = 0; + DWORD send_flags = flags; + int result = error_wrapper(::WSASend(s, send_bufs, + send_buf_count, &bytes_transferred, send_flags, 0, 0)); + if (result != 0) + return -1; + return bytes_transferred; +#else // defined(BOOST_WINDOWS) + if (count == 1) + { + // We only have to send a single buffer, so use normal send call. + return error_wrapper(::send(s, b[0].data, b[0].size, flags)); + } + else if (flags == 0) + { + // No flags have been specified so we can perform this send operation using + // a vectored-write function, i.e. writev. + + // Copy buffers into iovec array. + iovec send_bufs[max_bufs]; + if (count > max_bufs) + count = max_bufs; + for (size_t i = 0; i < count; ++i) + { + send_bufs[i].iov_len = b[i].size; + send_bufs[i].iov_base = b[i].data; + } + + // Send the data. + return error_wrapper(::writev(s, send_bufs, count)); + } + else + { + // Socket flags have been supplied so copy the caller-supplied buffers into + // a temporary buffer for sending. + + // Create a buffer of the appropriate size. + size_t buf_size = 0; + for (size_t i = 0; i < count; ++i) + buf_size += b[i].size; + std::vector buffer(buf_size); + + // Copy data from caller-supplied buffers. + using namespace std; // For memcpy. + size_t bytes_copied = 0; + for (size_t i = 0; i < count; ++i) + { + memcpy(&buffer[bytes_copied], b[i].data, b[i].size); + bytes_copied += b[i].size; + } + + // Receive some data. + return error_wrapper(::send(s, &buffer[0], buf_size, flags)); + } +#endif // defined(BOOST_WINDOWS) +} + +inline int sendto(socket_type s, const bufs* b, size_t count, int flags, + const socket_addr_type* addr, socket_addr_len_type addrlen) +{ + set_error(0); +#if defined(BOOST_WINDOWS) + // Copy buffers into WSABUF array. + WSABUF send_bufs[max_bufs]; + if (count > max_bufs) + count = max_bufs; + for (size_t i = 0; i < count; ++i) + { + send_bufs[i].len = static_cast(b[i].size); + send_bufs[i].buf = static_cast(b[i].data); + } + + // Send the data. + DWORD send_buf_count = static_cast(count); + DWORD bytes_transferred = 0; + int result = ::WSASendTo(s, send_bufs, send_buf_count, + &bytes_transferred, flags, addr, addrlen, 0, 0); + if (result != 0) + return -1; + return bytes_transferred; +#else // defined(BOOST_WINDOWS) + if (count == 1) + { + // We only have to send a single buffer, so use normal sendto call. + return error_wrapper(::sendto(s, + b[0].data, b[0].size, flags, addr, addrlen)); + } + else + { + // Socket flags have been supplied so copy the caller-supplied buffers into + // a temporary buffer for sending. + + // Create a buffer of the appropriate size. + size_t buf_size = 0; + for (size_t i = 0; i < count; ++i) + buf_size += b[i].size; + std::vector buffer(buf_size); + + // Copy data from caller-supplied buffers. + using namespace std; // For memcpy. + size_t bytes_copied = 0; + for (size_t i = 0; i < count; ++i) + { + memcpy(&buffer[bytes_copied], b[i].data, b[i].size); + bytes_copied += b[i].size; + } + + // Receive some data. + return error_wrapper(::sendto(s, + &buffer[0], buf_size, flags, addr, addrlen)); + } +#endif // defined(BOOST_WINDOWS) +} + +inline socket_type socket(int af, int type, int protocol) +{ + set_error(0); +#if defined(BOOST_WINDOWS) + return error_wrapper(::WSASocket(af, type, protocol, 0, 0, + WSA_FLAG_OVERLAPPED)); +#else // defined(BOOST_WINDOWS) + return error_wrapper(::socket(af, type, protocol)); +#endif // defined(BOOST_WINDOWS) +} + +inline int setsockopt(socket_type s, int level, int optname, + const void* optval, size_t optlen) +{ + set_error(0); +#if defined(BOOST_WINDOWS) + return error_wrapper(::setsockopt(s, level, optname, + reinterpret_cast(optval), static_cast(optlen))); +#else // defined(BOOST_WINDOWS) + return error_wrapper(::setsockopt(s, level, optname, optval, + static_cast(optlen))); +#endif // defined(BOOST_WINDOWS) +} + +inline int getsockopt(socket_type s, int level, int optname, void* optval, + size_t* optlen) +{ + set_error(0); +#if defined(BOOST_WINDOWS) + int tmp_optlen = static_cast(*optlen); + int result = error_wrapper(::getsockopt(s, level, optname, + reinterpret_cast(optval), &tmp_optlen)); + *optlen = static_cast(tmp_optlen); + return result; +#else // defined(BOOST_WINDOWS) + socklen_t tmp_optlen = static_cast(*optlen); + int result = error_wrapper(::getsockopt(s, level, optname, + optval, &tmp_optlen)); + *optlen = static_cast(tmp_optlen); + return result; +#endif // defined(BOOST_WINDOWS) +} + +inline int getpeername(socket_type s, socket_addr_type* addr, + socket_addr_len_type* addrlen) +{ + set_error(0); + return error_wrapper(::getpeername(s, addr, addrlen)); +} + +inline int getsockname(socket_type s, socket_addr_type* addr, + socket_addr_len_type* addrlen) +{ + set_error(0); + return error_wrapper(::getsockname(s, addr, addrlen)); +} + +inline int ioctl(socket_type s, long cmd, ioctl_arg_type* arg) +{ + set_error(0); +#if defined(BOOST_WINDOWS) + return error_wrapper(::ioctlsocket(s, cmd, arg)); +#else // defined(BOOST_WINDOWS) + return error_wrapper(::ioctl(s, cmd, arg)); +#endif // defined(BOOST_WINDOWS) +} + +inline int select(int nfds, fd_set* readfds, fd_set* writefds, + fd_set* exceptfds, timeval* timeout) +{ + set_error(0); +#if defined(BOOST_WINDOWS) + if (!readfds && !writefds && !exceptfds && timeout) + { + DWORD milliseconds = timeout->tv_sec * 1000 + timeout->tv_usec / 1000; + if (milliseconds == 0) + milliseconds = 1; // Force context switch. + ::Sleep(milliseconds); + return 0; + } +#endif // defined(BOOST_WINDOWS) + return error_wrapper(::select(nfds, readfds, writefds, exceptfds, timeout)); +} + +inline const char* inet_ntop(int af, const void* src, char* dest, + size_t length) +{ + set_error(0); +#if defined(BOOST_WINDOWS) + using namespace std; // For strncat. + + if (af != AF_INET) + { + set_error(boost::asio::error::address_family_not_supported); + return 0; + } + + char* addr_str = error_wrapper( + ::inet_ntoa(*static_cast(src))); + if (addr_str) + { + *dest = '\0'; +#if BOOST_WORKAROUND(BOOST_MSVC, >= 1400) + strncat_s(dest, length, addr_str, length); +#else + strncat(dest, addr_str, length); +#endif + return dest; + } + + // Windows may not set an error code on failure. + if (get_error() == 0) + set_error(boost::asio::error::invalid_argument); + + return 0; + +#else // defined(BOOST_WINDOWS) + const char* result = error_wrapper(::inet_ntop(af, src, dest, length)); + if (result == 0 && get_error() == 0) + set_error(boost::asio::error::invalid_argument); + return result; +#endif // defined(BOOST_WINDOWS) +} + +inline int inet_pton(int af, const char* src, void* dest) +{ + set_error(0); +#if defined(BOOST_WINDOWS) + using namespace std; // For strcmp. + + if (af != AF_INET) + { + set_error(boost::asio::error::address_family_not_supported); + return -1; + } + + u_long_type addr = error_wrapper(::inet_addr(src)); + if (addr != INADDR_NONE || strcmp(src, "255.255.255.255") == 0) + { + static_cast(dest)->s_addr = addr; + return 1; + } + + // Windows may not set an error code on failure. + if (get_error() == 0) + set_error(boost::asio::error::invalid_argument); + + return 0; +#else // defined(BOOST_WINDOWS) + int result = error_wrapper(::inet_pton(af, src, dest)); + if (result <= 0 && get_error() == 0) + set_error(boost::asio::error::invalid_argument); + return result; +#endif // defined(BOOST_WINDOWS) +} + +inline int gethostname(char* name, int namelen) +{ + set_error(0); + return error_wrapper(::gethostname(name, namelen)); +} + +inline int translate_netdb_error(int error) +{ + switch (error) + { + case 0: + return boost::asio::error::success; + case HOST_NOT_FOUND: + return boost::asio::error::host_not_found; + case TRY_AGAIN: + return boost::asio::error::host_not_found_try_again; + case NO_RECOVERY: + return boost::asio::error::no_recovery; + case NO_DATA: + return boost::asio::error::no_host_data; + default: + BOOST_ASSERT(false); + return get_error(); + } +} + +inline hostent* gethostbyaddr(const char* addr, int length, int type, + hostent* result, char* buffer, int buflength, int* error) +{ + set_error(0); +#if defined(BOOST_WINDOWS) + hostent* retval = error_wrapper(::gethostbyaddr(addr, length, type)); + *error = get_error(); + if (!retval) + return 0; + *result = *retval; + return retval; +#elif defined(__sun) + hostent* retval = error_wrapper(::gethostbyaddr_r(addr, length, type, result, + buffer, buflength, error)); + *error = translate_netdb_error(*error); + return retval; +#elif defined(__MACH__) && defined(__APPLE__) + hostent* retval = error_wrapper(::getipnodebyaddr(addr, length, type, error)); + *error = translate_netdb_error(*error); + if (!retval) + return 0; + *result = *retval; + return retval; +#else + hostent* retval = 0; + error_wrapper(::gethostbyaddr_r(addr, length, type, result, buffer, + buflength, &retval, error)); + *error = translate_netdb_error(*error); + return retval; +#endif +} + +inline hostent* gethostbyname(const char* name, struct hostent* result, + char* buffer, int buflength, int* error) +{ + set_error(0); +#if defined(BOOST_WINDOWS) + hostent* retval = error_wrapper(::gethostbyname(name)); + *error = get_error(); + if (!retval) + return 0; + *result = *retval; + return result; +#elif defined(__sun) + hostent* retval = error_wrapper(::gethostbyname_r(name, result, buffer, + buflength, error)); + *error = translate_netdb_error(*error); + return retval; +#elif defined(__MACH__) && defined(__APPLE__) + hostent* retval = error_wrapper(::getipnodebyname(name, AF_INET, 0, error)); + *error = translate_netdb_error(*error); + if (!retval) + return 0; + *result = *retval; + return retval; +#else + hostent* retval = 0; + error_wrapper(::gethostbyname_r(name, result, buffer, buflength, &retval, + error)); + *error = translate_netdb_error(*error); + return retval; +#endif +} + +inline void freehostent(hostent* h) +{ +#if defined(__MACH__) && defined(__APPLE__) + ::freehostent(h); +#endif +} + +inline u_long_type network_to_host_long(u_long_type value) +{ + return ntohl(value); +} + +inline u_long_type host_to_network_long(u_long_type value) +{ + return htonl(value); +} + +inline u_short_type network_to_host_short(u_short_type value) +{ + return ntohs(value); +} + +inline u_short_type host_to_network_short(u_short_type value) +{ + return htons(value); +} + +} // namespace socket_ops +} // namespace detail +} // namespace asio +} // namespace boost + +#include + +#endif // BOOST_ASIO_DETAIL_SOCKET_OPS_HPP diff --git a/include/boost/asio/detail/socket_option.hpp b/include/boost/asio/detail/socket_option.hpp new file mode 100644 index 00000000..5a70ec52 --- /dev/null +++ b/include/boost/asio/detail/socket_option.hpp @@ -0,0 +1,242 @@ +// +// socket_option.hpp +// ~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2005 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_DETAIL_SOCKET_OPTION_HPP +#define BOOST_ASIO_DETAIL_SOCKET_OPTION_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include + +#include +#include +#include +#include + +#include + +namespace boost { +namespace asio { +namespace detail { +namespace socket_option { + +// Helper template for implementing boolean-based options. +template +class boolean +{ +public: + // Default constructor. + boolean() + : value_(0) + { + } + + // Construct with a specific option value. + boolean(bool value) + : value_(value ? 1 : 0) + { + } + + // Get the level of the socket option. + int level() const + { + return Level; + } + + // Get the name of the socket option. + int name() const + { + return Name; + } + + // Set the value of the boolean. + void set(bool value) + { + value_ = value ? 1 : 0; + } + + // Get the current value of the boolean. + bool get() const + { + return value_; + } + + // Get the address of the boolean data. + int* data() + { + return &value_; + } + + // Get the address of the boolean data. + const int* data() const + { + return &value_; + } + + // Get the size of the boolean data. + std::size_t size() const + { + return sizeof(value_); + } + +private: + int value_; +}; + +// Helper template for implementing integer options. +template +class integer +{ +public: + // Default constructor. + integer() + : value_(0) + { + } + + // Construct with a specific option value. + integer(int value) + : value_(value) + { + } + + // Get the level of the socket option. + int level() const + { + return Level; + } + + // Get the name of the socket option. + int name() const + { + return Name; + } + + // Set the value of the int option. + void set(int value) + { + value_ = value; + } + + // Get the current value of the int option. + int get() const + { + return value_; + } + + // Get the address of the int data. + int* data() + { + return &value_; + } + + // Get the address of the int data. + const int* data() const + { + return &value_; + } + + // Get the size of the int data. + std::size_t size() const + { + return sizeof(value_); + } + +private: + int value_; +}; + +// Helper template for implementing linger options. +template +class linger +{ +public: + // Default constructor. + linger() + { + value_.l_onoff = 0; + value_.l_linger = 0; + } + + // Construct with specific option values. + linger(bool value, unsigned short timeout) + { + value_.l_onoff = value ? 1 : 0; + value_.l_linger = timeout; + } + + // Get the level of the socket option. + int level() const + { + return Level; + } + + // Get the name of the socket option. + int name() const + { + return Name; + } + + // Set the value for whether linger is enabled. + void enabled(bool value) + { + value_.l_onoff = value ? 1 : 0; + } + + // Get the value for whether linger is enabled. + bool enabled() const + { + return value_.l_onoff != 0; + } + + // Set the value for the linger timeout. + void timeout(unsigned short value) + { + value_.l_linger = value; + } + + // Get the value for the linger timeout. + unsigned short timeout() const + { + return value_.l_linger; + } + + // Get the address of the linger data. + ::linger* data() + { + return &value_; + } + + // Get the address of the linger data. + const ::linger* data() const + { + return &value_; + } + + // Get the size of the linger data. + std::size_t size() const + { + return sizeof(value_); + } + +private: + ::linger value_; +}; + +} // namespace socket_option +} // namespace detail +} // namespace asio +} // namespace boost + +#include + +#endif // BOOST_ASIO_DETAIL_SOCKET_OPTION_HPP diff --git a/include/boost/asio/detail/socket_select_interrupter.hpp b/include/boost/asio/detail/socket_select_interrupter.hpp new file mode 100644 index 00000000..ac9f1458 --- /dev/null +++ b/include/boost/asio/detail/socket_select_interrupter.hpp @@ -0,0 +1,181 @@ +// +// socket_select_interrupter.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2005 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_DETAIL_SOCKET_SELECT_INTERRUPTER_HPP +#define BOOST_ASIO_DETAIL_SOCKET_SELECT_INTERRUPTER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include + +#include +#include +#include + +#include +#include +#include +#include + +namespace boost { +namespace asio { +namespace detail { + +class socket_select_interrupter +{ +public: + // Constructor. + socket_select_interrupter() + { + socket_holder acceptor(socket_ops::socket(AF_INET, SOCK_STREAM, + IPPROTO_TCP)); + if (acceptor.get() == invalid_socket) + { + boost::asio::error e(socket_ops::get_error()); + boost::throw_exception(e); + } + + int opt = 1; + socket_ops::setsockopt(acceptor.get(), + SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); + + inet_addr_v4_type addr; + socket_addr_len_type addr_len = sizeof(addr); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = inet_addr("127.0.0.1"); + addr.sin_port = 0; + if (socket_ops::bind(acceptor.get(), (const socket_addr_type*)&addr, + addr_len) == socket_error_retval) + { + boost::asio::error e(socket_ops::get_error()); + boost::throw_exception(e); + } + + if (getsockname(acceptor.get(), (socket_addr_type*)&addr, &addr_len) + == socket_error_retval) + { + boost::asio::error e(socket_ops::get_error()); + boost::throw_exception(e); + } + + if (socket_ops::listen(acceptor.get(), SOMAXCONN) == socket_error_retval) + { + boost::asio::error e(socket_ops::get_error()); + boost::throw_exception(e); + } + + socket_holder client(socket_ops::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)); + if (client.get() == invalid_socket) + { + boost::asio::error e(socket_ops::get_error()); + boost::throw_exception(e); + } + + if (socket_ops::connect(client.get(), (const socket_addr_type*)&addr, + addr_len) == socket_error_retval) + { + boost::asio::error e(socket_ops::get_error()); + boost::throw_exception(e); + } + + socket_holder server(socket_ops::accept(acceptor.get(), 0, 0)); + if (server.get() == invalid_socket) + { + boost::asio::error e(socket_ops::get_error()); + boost::throw_exception(e); + } + + ioctl_arg_type non_blocking = 1; + if (socket_ops::ioctl(client.get(), FIONBIO, &non_blocking)) + { + boost::asio::error e(socket_ops::get_error()); + boost::throw_exception(e); + } + + opt = 1; + socket_ops::setsockopt(client.get(), + IPPROTO_TCP, TCP_NODELAY, &opt, sizeof(opt)); + + non_blocking = 1; + if (socket_ops::ioctl(server.get(), FIONBIO, &non_blocking)) + { + boost::asio::error e(socket_ops::get_error()); + boost::throw_exception(e); + } + + opt = 1; + socket_ops::setsockopt(server.get(), + IPPROTO_TCP, TCP_NODELAY, &opt, sizeof(opt)); + + read_descriptor_ = server.release(); + write_descriptor_ = client.release(); + } + + // Destructor. + ~socket_select_interrupter() + { + if (read_descriptor_ != invalid_socket) + socket_ops::close(read_descriptor_); + if (write_descriptor_ != invalid_socket) + socket_ops::close(write_descriptor_); + } + + // Interrupt the select call. + void interrupt() + { + char byte = 0; + socket_ops::bufs b; + b.data = &byte; + b.size = 1; + socket_ops::send(write_descriptor_, &b, 1, 0); + } + + // Reset the select interrupt. Returns true if the call was interrupted. + bool reset() + { + char data[1024]; + socket_ops::bufs b; + b.data = data; + b.size = sizeof(data); + int bytes_read = socket_ops::recv(read_descriptor_, &b, 1, 0); + bool was_interrupted = (bytes_read > 0); + while (bytes_read == sizeof(data)) + bytes_read = socket_ops::recv(read_descriptor_, &b, 1, 0); + return was_interrupted; + } + + // Get the read descriptor to be passed to select. + socket_type read_descriptor() const + { + return read_descriptor_; + } + +private: + // The read end of a connection used to interrupt the select call. This file + // descriptor is passed to select such that when it is time to stop, a single + // byte will be written on the other end of the connection and this + // descriptor will become readable. + socket_type read_descriptor_; + + // The write end of a connection used to interrupt the select call. A single + // byte may be written to this to wake up the select which is waiting for the + // other end to become readable. + socket_type write_descriptor_; +}; + +} // namespace detail +} // namespace asio +} // namespace boost + +#include + +#endif // BOOST_ASIO_DETAIL_SOCKET_SELECT_INTERRUPTER_HPP diff --git a/include/boost/asio/detail/socket_types.hpp b/include/boost/asio/detail/socket_types.hpp new file mode 100644 index 00000000..55df6adc --- /dev/null +++ b/include/boost/asio/detail/socket_types.hpp @@ -0,0 +1,115 @@ +// +// socket_types.hpp +// ~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2005 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_DETAIL_SOCKET_TYPES_HPP +#define BOOST_ASIO_DETAIL_SOCKET_TYPES_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include + +#include +#include +#include + +#include +#if defined(BOOST_WINDOWS) +# if !defined(_WIN32_WINNT) && !defined(_WIN32_WINDOWS) +# if defined(_MSC_VER) || defined(__BORLANDC__) +# pragma message("Please define _WIN32_WINNT or _WIN32_WINDOWS appropriately") +# pragma message("Assuming _WIN32_WINNT=0x0500 (i.e. Windows 2000 target)") +# else // defined(_MSC_VER) || defined(__BORLANDC__) +# warning Please define _WIN32_WINNT or _WIN32_WINDOWS appropriately +# warning Assuming _WIN32_WINNT=0x0500 (i.e. Windows 2000 target) +# endif // defined(_MSC_VER) || defined(__BORLANDC__) +# define _WIN32_WINNT 0x0500 +# endif // !defined(_WIN32_WINNT) && !defined(_WIN32_WINDOWS) +# if defined(__BORLANDC__) && !defined(_WSPIAPI_H_) +# include // Needed for __errno +# define _WSPIAPI_H_ +# define BOOST_ASIO_WSPIAPI_H_DEFINED +# endif // defined(__BORLANDC__) && !defined(_WSPIAPI_H_) +# define FD_SETSIZE 1024 +# include +# include +# include +# if defined(BOOST_ASIO_WSPIAPI_H_DEFINED) +# undef _WSPIAPI_H_ +# undef BOOST_ASIO_WSPIAPI_H_DEFINED +# endif // defined(BOOST_ASIO_WSPIAPI_H_DEFINED) +# if defined(_MSC_VER) || defined(__BORLANDC__) +# pragma comment(lib, "ws2_32.lib") +# pragma comment(lib, "mswsock.lib") +# endif // defined(_MSC_VER) || defined(__BORLANDC__) +#else +# include +# include +# include +# include +# include +# include +# include +# include +# include +# if defined(__sun) +# include +# endif +#endif +#include + +namespace boost { +namespace asio { +namespace detail { + +#if defined(BOOST_WINDOWS) +typedef SOCKET socket_type; +const SOCKET invalid_socket = INVALID_SOCKET; +const int socket_error_retval = SOCKET_ERROR; +const int max_addr_str_len = 256; +typedef sockaddr socket_addr_type; +typedef sockaddr_in inet_addr_v4_type; +typedef int socket_addr_len_type; +typedef unsigned long ioctl_arg_type; +typedef u_long u_long_type; +typedef u_short u_short_type; +const int shutdown_receive = SD_RECEIVE; +const int shutdown_send = SD_SEND; +const int shutdown_both = SD_BOTH; +const int message_peek = MSG_PEEK; +const int message_out_of_band = MSG_OOB; +const int message_do_not_route = MSG_DONTROUTE; +#else +typedef int socket_type; +const int invalid_socket = -1; +const int socket_error_retval = -1; +const int max_addr_str_len = INET_ADDRSTRLEN; +typedef sockaddr socket_addr_type; +typedef sockaddr_in inet_addr_v4_type; +typedef socklen_t socket_addr_len_type; +typedef int ioctl_arg_type; +typedef uint32_t u_long_type; +typedef uint16_t u_short_type; +const int shutdown_receive = SHUT_RD; +const int shutdown_send = SHUT_WR; +const int shutdown_both = SHUT_RDWR; +const int message_peek = MSG_PEEK; +const int message_out_of_band = MSG_OOB; +const int message_do_not_route = MSG_DONTROUTE; +#endif + +} // namespace detail +} // namespace asio +} // namespace boost + +#include + +#endif // BOOST_ASIO_DETAIL_SOCKET_TYPES_HPP diff --git a/include/boost/asio/detail/task_demuxer_service.hpp b/include/boost/asio/detail/task_demuxer_service.hpp new file mode 100644 index 00000000..2de9af77 --- /dev/null +++ b/include/boost/asio/detail/task_demuxer_service.hpp @@ -0,0 +1,377 @@ +// +// task_demuxer_service.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2005 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_DETAIL_TASK_DEMUXER_SERVICE_HPP +#define BOOST_ASIO_DETAIL_TASK_DEMUXER_SERVICE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace boost { +namespace asio { +namespace detail { + +template +class task_demuxer_service +{ +public: + // Constructor. + template + task_demuxer_service(Demuxer& demuxer) + : mutex_(), + task_(demuxer.get_service(service_factory())), + task_is_running_(false), + outstanding_work_(0), + handler_queue_(0), + handler_queue_end_(0), + interrupted_(false), + first_idle_thread_(0) + { + } + + // Run the demuxer's event processing loop. + void run() + { + typename demuxer_run_call_stack::context ctx(this); + + idle_thread_info this_idle_thread; + this_idle_thread.prev = &this_idle_thread; + this_idle_thread.next = &this_idle_thread; + + boost::asio::detail::mutex::scoped_lock lock(mutex_); + + while (!interrupted_ && outstanding_work_ > 0) + { + if (handler_queue_) + { + // Prepare to execute first handler from queue. + handler_base* h = handler_queue_; + handler_queue_ = h->next_; + if (handler_queue_ == 0) + handler_queue_end_ = 0; + lock.unlock(); + + // Helper class to perform operations on block exit. + class cleanup + { + public: + cleanup(boost::asio::detail::mutex::scoped_lock& lock, + int& outstanding_work) + : lock_(lock), + outstanding_work_(outstanding_work) + { + } + + ~cleanup() + { + lock_.lock(); + --outstanding_work_; + } + + private: + boost::asio::detail::mutex::scoped_lock& lock_; + int& outstanding_work_; + } c(lock, outstanding_work_); + + // Invoke the handler. May throw an exception. + h->call(); // call() deletes the handler object + } + else if (!task_is_running_) + { + // Prepare to execute the task. + task_is_running_ = true; + task_.reset(); + lock.unlock(); + + // Helper class to perform operations on block exit. + class cleanup + { + public: + cleanup(boost::asio::detail::mutex::scoped_lock& lock, + bool& task_is_running) + : lock_(lock), + task_is_running_(task_is_running) + { + } + + ~cleanup() + { + lock_.lock(); + task_is_running_ = false; + } + + private: + boost::asio::detail::mutex::scoped_lock& lock_; + bool& task_is_running_; + } c(lock, task_is_running_); + + // Run the task. May throw an exception. + task_.run(); + } + else + { + // Nothing to run right now, so just wait for work to do. + if (first_idle_thread_) + { + this_idle_thread.next = first_idle_thread_; + this_idle_thread.prev = first_idle_thread_->prev; + first_idle_thread_->prev->next = &this_idle_thread; + first_idle_thread_->prev = &this_idle_thread; + } + first_idle_thread_ = &this_idle_thread; + this_idle_thread.wakeup_event.clear(); + lock.unlock(); + this_idle_thread.wakeup_event.wait(); + lock.lock(); + if (this_idle_thread.next == &this_idle_thread) + { + first_idle_thread_ = 0; + } + else + { + if (first_idle_thread_ == &this_idle_thread) + first_idle_thread_ = this_idle_thread.next; + this_idle_thread.next->prev = this_idle_thread.prev; + this_idle_thread.prev->next = this_idle_thread.next; + this_idle_thread.next = &this_idle_thread; + this_idle_thread.prev = &this_idle_thread; + } + } + } + + if (!interrupted_) + { + // No more work to do! + interrupt_all_threads(); + } + } + + // Interrupt the demuxer's event processing loop. + void interrupt() + { + boost::asio::detail::mutex::scoped_lock lock(mutex_); + interrupt_all_threads(); + } + + // Reset the demuxer in preparation for a subsequent run invocation. + void reset() + { + boost::asio::detail::mutex::scoped_lock lock(mutex_); + interrupted_ = false; + } + + // Notify the demuxer that some work has started. + void work_started() + { + boost::asio::detail::mutex::scoped_lock lock(mutex_); + ++outstanding_work_; + } + + // Notify the demuxer that some work has finished. + void work_finished() + { + boost::asio::detail::mutex::scoped_lock lock(mutex_); + if (--outstanding_work_ == 0) + interrupt_all_threads(); + } + + // Request the demuxer to invoke the given handler. + template + void dispatch(Handler handler) + { + if (demuxer_run_call_stack::contains(this)) + handler(); + else + post(handler); + } + + // Request the demuxer to invoke the given handler and return immediately. + template + void post(Handler handler) + { + boost::asio::detail::mutex::scoped_lock lock(mutex_); + + // Add the handler to the end of the queue. + handler_base* h = new handler_wrapper(handler); + if (handler_queue_end_) + { + handler_queue_end_->next_ = h; + handler_queue_end_ = h; + } + else + { + handler_queue_ = handler_queue_end_ = h; + } + + // An undelivered handler is treated as unfinished work. + ++outstanding_work_; + + // Wake up a thread to execute the handler. + if (!interrupt_one_idle_thread()) + interrupt_task(); + } + +private: + // Interrupt the task and all idle threads. + void interrupt_all_threads() + { + interrupted_ = true; + interrupt_all_idle_threads(); + interrupt_task(); + } + + // Interrupt a single idle thread. Returns true if a thread was interrupted, + // false if no running thread could be found to interrupt. + bool interrupt_one_idle_thread() + { + if (first_idle_thread_) + { + first_idle_thread_->wakeup_event.signal(); + first_idle_thread_ = first_idle_thread_->next; + return true; + } + return false; + } + + // Interrupt all idle threads. + void interrupt_all_idle_threads() + { + if (first_idle_thread_) + { + first_idle_thread_->wakeup_event.signal(); + idle_thread_info* current_idle_thread = first_idle_thread_->next; + while (current_idle_thread != first_idle_thread_) + { + current_idle_thread->wakeup_event.signal(); + current_idle_thread = current_idle_thread->next; + } + } + } + + // Interrupt the task. Returns true if the task was interrupted, false if + // the task was not running and so could not be interrupted. + bool interrupt_task() + { + if (task_is_running_) + { + task_.interrupt(); + return true; + } + return false; + } + + // The base class for all handler wrappers. A function pointer is used + // instead of virtual functions to avoid the associated overhead. + class handler_base + { + public: + typedef void (*func_type)(handler_base*); + + handler_base(func_type func) + : next_(0), + func_(func) + { + } + + void call() + { + func_(this); + } + + protected: + // Prevent deletion through this type. + ~handler_base() + { + } + + private: + friend class task_demuxer_service; + handler_base* next_; + func_type func_; + }; + + // Template wrapper for handlers. + template + class handler_wrapper + : public handler_base + { + public: + handler_wrapper(Handler handler) + : handler_base(&handler_wrapper::do_call), + handler_(handler) + { + } + + static void do_call(handler_base* base) + { + std::auto_ptr > h( + static_cast*>(base)); + h->handler_(); + } + + private: + Handler handler_; + }; + + // Mutex to protect access to internal data. + boost::asio::detail::mutex mutex_; + + // The task to be run by this demuxer service. + Task& task_; + + // Whether the task is currently running. + bool task_is_running_; + + // The count of unfinished work. + int outstanding_work_; + + // The start of a linked list of handlers that are ready to be delivered. + handler_base* handler_queue_; + + // The end of a linked list of handlers that are ready to be delivered. + handler_base* handler_queue_end_; + + // Flag to indicate that the dispatcher has been interrupted. + bool interrupted_; + + // Structure containing information about an idle thread. + struct idle_thread_info + { + event wakeup_event; + idle_thread_info* prev; + idle_thread_info* next; + }; + + // The number of threads that are currently idle. + idle_thread_info* first_idle_thread_; +}; + +} // namespace detail +} // namespace asio +} // namespace boost + +#include + +#endif // BOOST_ASIO_DETAIL_TASK_DEMUXER_SERVICE_HPP diff --git a/include/boost/asio/detail/thread.hpp b/include/boost/asio/detail/thread.hpp new file mode 100644 index 00000000..30e60300 --- /dev/null +++ b/include/boost/asio/detail/thread.hpp @@ -0,0 +1,52 @@ +// +// thread.hpp +// ~~~~~~~~~~ +// +// Copyright (c) 2003-2005 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_DETAIL_THREAD_HPP +#define BOOST_ASIO_DETAIL_THREAD_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include + +#include +#include +#include + +#if !defined(BOOST_HAS_THREADS) +# error Thread support is required! +#endif + +#if defined(BOOST_WINDOWS) +# include +#elif defined(BOOST_HAS_PTHREADS) +# include +#else +# error Only Windows and POSIX are supported! +#endif + +namespace boost { +namespace asio { +namespace detail { + +#if defined(BOOST_WINDOWS) +typedef win_thread thread; +#elif defined(BOOST_HAS_PTHREADS) +typedef posix_thread thread; +#endif + +} // namespace detail +} // namespace asio +} // namespace boost + +#include + +#endif // BOOST_ASIO_DETAIL_THREAD_HPP diff --git a/include/boost/asio/detail/tss_ptr.hpp b/include/boost/asio/detail/tss_ptr.hpp new file mode 100644 index 00000000..2de4b132 --- /dev/null +++ b/include/boost/asio/detail/tss_ptr.hpp @@ -0,0 +1,65 @@ +// +// tss_ptr.hpp +// ~~~~~~~~~~~ +// +// Copyright (c) 2003-2005 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_DETAIL_TSS_PTR_HPP +#define BOOST_ASIO_DETAIL_TSS_PTR_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include + +#include +#include +#include + +#if !defined(BOOST_HAS_THREADS) +# error Thread support is required! +#endif + +#if defined(BOOST_WINDOWS) +# include +#elif defined(BOOST_HAS_PTHREADS) +# include +#else +# error Only Windows and POSIX are supported! +#endif + +namespace boost { +namespace asio { +namespace detail { + +template +class tss_ptr +#if defined(BOOST_WINDOWS) + : public win_tss_ptr +#elif defined(BOOST_HAS_PTHREADS) + : public posix_tss_ptr +#endif +{ +public: + void operator=(T* value) + { +#if defined(BOOST_WINDOWS) + win_tss_ptr::operator=(value); +#elif defined(BOOST_HAS_PTHREADS) + posix_tss_ptr::operator=(value); +#endif + } +}; + +} // namespace detail +} // namespace asio +} // namespace boost + +#include + +#endif // BOOST_ASIO_DETAIL_TSS_PTR_HPP diff --git a/include/boost/asio/detail/win_event.hpp b/include/boost/asio/detail/win_event.hpp new file mode 100644 index 00000000..d078e9c0 --- /dev/null +++ b/include/boost/asio/detail/win_event.hpp @@ -0,0 +1,90 @@ +// +// win_event.hpp +// ~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2005 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_DETAIL_WIN_EVENT_HPP +#define BOOST_ASIO_DETAIL_WIN_EVENT_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include + +#include +#include +#include + +#if defined(BOOST_WINDOWS) + +#include +#include +#include + +#include +#include +#include + +namespace boost { +namespace asio { +namespace detail { + +class win_event + : private noncopyable +{ +public: + // Constructor. + win_event() + : event_(::CreateEvent(0, true, false, 0)) + { + if (!event_) + { + DWORD last_error = ::GetLastError(); + system_exception e("event", last_error); + boost::throw_exception(e); + } + } + + // Destructor. + ~win_event() + { + ::CloseHandle(event_); + } + + // Signal the event. + void signal() + { + ::SetEvent(event_); + } + + // Reset the event. + void clear() + { + ::ResetEvent(event_); + } + + // Wait for the event to become signalled. + void wait() + { + ::WaitForSingleObject(event_, INFINITE); + } + +private: + HANDLE event_; +}; + +} // namespace detail +} // namespace asio +} // namespace boost + +#endif // defined(BOOST_WINDOWS) + +#include + +#endif // BOOST_ASIO_DETAIL_WIN_EVENT_HPP diff --git a/include/boost/asio/detail/win_iocp_demuxer_service.hpp b/include/boost/asio/detail/win_iocp_demuxer_service.hpp new file mode 100644 index 00000000..1c135273 --- /dev/null +++ b/include/boost/asio/detail/win_iocp_demuxer_service.hpp @@ -0,0 +1,216 @@ +// +// win_iocp_demuxer_service.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2005 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_DETAIL_WIN_IOCP_DEMUXER_SERVICE_HPP +#define BOOST_ASIO_DETAIL_WIN_IOCP_DEMUXER_SERVICE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include + +#include +#include +#include + +// This service is only supported on Win32 (NT4 and later). +#if defined(BOOST_WINDOWS) +#if defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0400) + +// Define this to indicate that IOCP is supported on the target platform. +#define BOOST_ASIO_HAS_IOCP_DEMUXER 1 + +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace boost { +namespace asio { +namespace detail { + +class win_iocp_demuxer_service +{ +public: + // Constructor. + template + win_iocp_demuxer_service(Demuxer& demuxer) + : iocp_(::CreateIoCompletionPort(INVALID_HANDLE_VALUE, 0, 0, 0)), + outstanding_work_(0), + interrupted_(0) + { + if (!iocp_.handle) + { + DWORD last_error = ::GetLastError(); + system_exception e("iocp", last_error); + boost::throw_exception(e); + } + } + + // Register a socket with the demuxer. + void register_socket(socket_type sock) + { + HANDLE sock_as_handle = reinterpret_cast(sock); + ::CreateIoCompletionPort(sock_as_handle, iocp_.handle, 0, 0); + } + + // Run the demuxer's event processing loop. + void run() + { + if (::InterlockedExchangeAdd(&outstanding_work_, 0) == 0) + return; + + demuxer_run_call_stack::context ctx(this); + + for (;;) + { + // Get the next operation from the queue. + DWORD bytes_transferred = 0; +#if (WINVER < 0x0500) + DWORD completion_key = 0; +#else + DWORD_PTR completion_key = 0; +#endif + LPOVERLAPPED overlapped = 0; + ::SetLastError(0); + ::GetQueuedCompletionStatus(iocp_.handle, &bytes_transferred, + &completion_key, &overlapped, INFINITE); + DWORD last_error = ::GetLastError(); + + if (overlapped) + { + // Dispatch the operation. + win_iocp_operation* op = static_cast(overlapped); + op->do_completion(last_error, bytes_transferred); + } + else + { + // The interrupted_ flag is always checked to ensure that any leftover + // interrupts from a previous run invocation are ignored. + if (::InterlockedExchangeAdd(&interrupted_, 0) != 0) + { + // Wake up next thread that is blocked on GetQueuedCompletionStatus. + ::PostQueuedCompletionStatus(iocp_.handle, 0, 0, 0); + break; + } + } + } + } + + // Interrupt the demuxer's event processing loop. + void interrupt() + { + if (::InterlockedExchange(&interrupted_, 1) == 0) + ::PostQueuedCompletionStatus(iocp_.handle, 0, 0, 0); + } + + // Reset the demuxer in preparation for a subsequent run invocation. + void reset() + { + ::InterlockedExchange(&interrupted_, 0); + } + + // Notify the demuxer that some work has started. + void work_started() + { + ::InterlockedIncrement(&outstanding_work_); + } + + // Notify the demuxer that some work has finished. + void work_finished() + { + if (::InterlockedDecrement(&outstanding_work_) == 0) + interrupt(); + } + + template + struct handler_operation + : public win_iocp_operation + { + handler_operation(win_iocp_demuxer_service& demuxer_service, + Handler handler) + : win_iocp_operation(&handler_operation::do_completion_impl), + demuxer_service_(demuxer_service), + handler_(handler) + { + demuxer_service_.work_started(); + } + + ~handler_operation() + { + demuxer_service_.work_finished(); + } + + private: + // Prevent copying and assignment. + handler_operation(const handler_operation&); + void operator=(const handler_operation&); + + static void do_completion_impl(win_iocp_operation* op, DWORD, size_t) + { + std::auto_ptr > h( + static_cast*>(op)); + h->handler_(); + } + + win_iocp_demuxer_service& demuxer_service_; + Handler handler_; + }; + + // Request the demuxer to invoke the given handler. + template + void dispatch(Handler handler) + { + if (demuxer_run_call_stack::contains(this)) + handler(); + else + post(handler); + } + + // Request the demuxer to invoke the given handler and return immediately. + template + void post(Handler handler) + { + win_iocp_operation* op = new handler_operation(*this, handler); + ::PostQueuedCompletionStatus(iocp_.handle, 0, 0, op); + } + +private: + // The IO completion port used for queueing operations. + struct iocp_holder + { + HANDLE handle; + iocp_holder(HANDLE h) : handle(h) {} + ~iocp_holder() { ::CloseHandle(handle); } + } iocp_; + + // The count of unfinished work. + long outstanding_work_; + + // Flag to indicate whether the event loop has been interrupted. + long interrupted_; +}; + +} // namespace detail +} // namespace asio +} // namespace boost + +#endif // defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0400) +#endif // defined(BOOST_WINDOWS) + +#include + +#endif // BOOST_ASIO_DETAIL_WIN_IOCP_DEMUXER_SERVICE_HPP diff --git a/include/boost/asio/detail/win_iocp_operation.hpp b/include/boost/asio/detail/win_iocp_operation.hpp new file mode 100644 index 00000000..e7d435bb --- /dev/null +++ b/include/boost/asio/detail/win_iocp_operation.hpp @@ -0,0 +1,81 @@ +// +// win_iocp_operation.hpp +// ~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2005 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_DETAIL_WIN_IOCP_OPERATION_HPP +#define BOOST_ASIO_DETAIL_WIN_IOCP_OPERATION_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include + +#include +#include +#include + +// This class is only supported on Win32 (NT4 and later). +#if defined(BOOST_WINDOWS) +#if defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0400) + +#include + +namespace boost { +namespace asio { +namespace detail { + +class win_iocp_demuxer_service; + +// Base class for all IOCP operations. A function pointer is used instead of +// virtual functions to avoid the associated overhead. +// +// This class inherits from OVERLAPPED so that we can downcast to get back to +// the win_iocp_operation pointer from the LPOVERLAPPED out parameter of +// GetQueuedCompletionStatus. +struct win_iocp_operation + : public OVERLAPPED +{ + typedef void (*func_type)(win_iocp_operation*, DWORD, size_t); + + win_iocp_operation(func_type func) + : func_(func) + { + Internal = 0; + InternalHigh = 0; + Offset = 0; + OffsetHigh = 0; + hEvent = 0; + } + + void do_completion(DWORD last_error, size_t bytes_transferred) + { + func_(this, last_error, bytes_transferred); + } + +protected: + // Prevent deletion through this type. + ~win_iocp_operation() + { + } + +private: + func_type func_; +}; + +} // namespace detail +} // namespace asio +} // namespace boost + +#endif // defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0400) +#endif // defined(BOOST_WINDOWS) + +#include + +#endif // BOOST_ASIO_DETAIL_WIN_IOCP_OPERATION_HPP diff --git a/include/boost/asio/detail/win_iocp_socket_service.hpp b/include/boost/asio/detail/win_iocp_socket_service.hpp new file mode 100644 index 00000000..0830a830 --- /dev/null +++ b/include/boost/asio/detail/win_iocp_socket_service.hpp @@ -0,0 +1,1285 @@ +// +// win_iocp_socket_service.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2005 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_DETAIL_WIN_IOCP_SOCKET_SERVICE_HPP +#define BOOST_ASIO_DETAIL_WIN_IOCP_SOCKET_SERVICE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include + +#include +#include +#include + +// This service is only supported on Win32 (NT4 and later). +#if defined(BOOST_WINDOWS) +#if defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0400) + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace boost { +namespace asio { +namespace detail { + +template +class win_iocp_socket_service +{ +public: + typedef boost::shared_ptr socket_shared_ptr_type; + typedef boost::weak_ptr socket_weak_ptr_type; + + // The native type of the socket. We use a custom class here rather than just + // SOCKET to workaround the broken Windows support for cancellation. MSDN says + // that when you call closesocket any outstanding WSARecv or WSASend + // operations will complete with the error ERROR_OPERATION_ABORTED. In + // practice they complete with ERROR_NETNAME_DELETED, which means you can't + // tell the difference between a local cancellation and the socket being + // hard-closed by the peer. + class impl_type + { + public: + // Default constructor. + impl_type() + : socket_(new socket_type(invalid_socket)) + { + } + + // Construct from socket type. + explicit impl_type(socket_type s) + : socket_(new socket_type(s)) + { + } + + // Copy constructor. + impl_type(const impl_type& other) + : socket_(other.socket_) + { + } + + // Assignment operator. + impl_type& operator=(const impl_type& other) + { + socket_ = other.socket_; + return *this; + } + + // Assign from socket type. + impl_type& operator=(socket_type s) + { + socket_ = socket_shared_ptr_type(new socket_type(s)); + return *this; + } + + // Convert to socket type. + operator socket_type() const + { + return *socket_; + } + + private: + friend class win_iocp_socket_service; + socket_shared_ptr_type socket_; + }; + + // The demuxer type for this service. + typedef basic_demuxer > demuxer_type; + + // The type of the reactor used for connect operations. + typedef detail::select_reactor reactor_type; + + // The maximum number of buffers to support in a single operation. + enum { max_buffers = 16 }; + + // Constructor. This socket service can only work if the demuxer is + // using the win_iocp_demuxer_service. By using this type as the parameter we + // will cause a compile error if this is not the case. + win_iocp_socket_service( + demuxer_type& demuxer) + : demuxer_(demuxer), + demuxer_service_(demuxer.get_service( + service_factory())), + reactor_(demuxer.get_service(service_factory())) + { + } + + // Get the demuxer associated with the service. + demuxer_type& demuxer() + { + return demuxer_; + } + + // Return a null socket implementation. + static impl_type null() + { + return impl_type(); + } + + // Open a new socket implementation. + template + void open(impl_type& impl, const Protocol& protocol, + Error_Handler error_handler) + { + socket_holder sock(socket_ops::socket(protocol.family(), protocol.type(), + protocol.protocol())); + if (sock.get() == invalid_socket) + { + error_handler(boost::asio::error(socket_ops::get_error())); + return; + } + + demuxer_service_.register_socket(sock.get()); + + impl = sock.release(); + } + + // Assign a new socket implementation. + void assign(impl_type& impl, impl_type new_impl) + { + demuxer_service_.register_socket(new_impl); + impl = new_impl; + } + + // Destroy a socket implementation. + template + void close(impl_type& impl, Error_Handler error_handler) + { + if (impl != null()) + { + reactor_.close_descriptor(impl); + if (socket_ops::close(impl) == socket_error_retval) + error_handler(boost::asio::error(socket_ops::get_error())); + else + impl = null(); + } + } + + // Bind the socket to the specified local endpoint. + template + void bind(impl_type& impl, const Endpoint& endpoint, + Error_Handler error_handler) + { + if (socket_ops::bind(impl, endpoint.data(), + endpoint.size()) == socket_error_retval) + error_handler(boost::asio::error(socket_ops::get_error())); + } + + // Place the socket into the state where it will listen for new connections. + template + void listen(impl_type& impl, int backlog, Error_Handler error_handler) + { + if (backlog == 0) + backlog = SOMAXCONN; + + if (socket_ops::listen(impl, backlog) == socket_error_retval) + error_handler(boost::asio::error(socket_ops::get_error())); + } + + // Set a socket option. + template + void set_option(impl_type& impl, const Option& option, + Error_Handler error_handler) + { + if (socket_ops::setsockopt(impl, option.level(), option.name(), + option.data(), option.size())) + error_handler(boost::asio::error(socket_ops::get_error())); + } + + // Set a socket option. + template + void get_option(const impl_type& impl, Option& option, + Error_Handler error_handler) const + { + size_t size = option.size(); + if (socket_ops::getsockopt(impl, option.level(), option.name(), + option.data(), &size)) + error_handler(boost::asio::error(socket_ops::get_error())); + } + + // Perform an IO control command on the socket. + template + void io_control(impl_type& impl, IO_Control_Command& command, + Error_Handler error_handler) + { + if (socket_ops::ioctl(impl, command.name(), + static_cast(command.data()))) + error_handler(boost::asio::error(socket_ops::get_error())); + } + + // Get the local endpoint. + template + void get_local_endpoint(const impl_type& impl, Endpoint& endpoint, + Error_Handler error_handler) const + { + socket_addr_len_type addr_len = endpoint.size(); + if (socket_ops::getsockname(impl, endpoint.data(), &addr_len)) + { + error_handler(boost::asio::error(socket_ops::get_error())); + return; + } + + endpoint.size(addr_len); + } + + // Get the remote endpoint. + template + void get_remote_endpoint(const impl_type& impl, Endpoint& endpoint, + Error_Handler error_handler) const + { + socket_addr_len_type addr_len = endpoint.size(); + if (socket_ops::getpeername(impl, endpoint.data(), &addr_len)) + { + error_handler(boost::asio::error(socket_ops::get_error())); + return; + } + + endpoint.size(addr_len); + } + + /// Disable sends or receives on the socket. + template + void shutdown(impl_type& impl, socket_base::shutdown_type what, + Error_Handler error_handler) + { + if (socket_ops::shutdown(impl, what) != 0) + error_handler(boost::asio::error(socket_ops::get_error())); + } + + // Send the given data to the peer. Returns the number of bytes sent or + // 0 if the connection was closed cleanly. + template + size_t send(impl_type& impl, const Const_Buffers& buffers, + socket_base::message_flags flags, Error_Handler error_handler) + { + // Copy buffers into WSABUF array. + ::WSABUF bufs[max_buffers]; + typename Const_Buffers::const_iterator iter = buffers.begin(); + typename Const_Buffers::const_iterator end = buffers.end(); + DWORD i = 0; + for (; iter != end && i < max_buffers; ++iter, ++i) + { + bufs[i].len = static_cast(boost::asio::buffer_size(*iter)); + bufs[i].buf = const_cast( + boost::asio::buffer_cast(*iter)); + } + + // Send the data. + DWORD bytes_transferred = 0; + int result = ::WSASend(impl, bufs, i, &bytes_transferred, flags, 0, 0); + if (result != 0) + { + DWORD last_error = ::WSAGetLastError(); + if (last_error == ERROR_NETNAME_DELETED) + last_error = WSAECONNRESET; + error_handler(boost::asio::error(last_error)); + return 0; + } + + return bytes_transferred; + } + + template + class send_operation + : public win_iocp_operation + { + public: + send_operation(demuxer_type& demuxer, + socket_weak_ptr_type socket_ptr, Handler handler) + : win_iocp_operation(&send_operation::do_completion_impl), + work_(demuxer), + socket_ptr_(socket_ptr), + handler_(handler) + { + } + + private: + static void do_completion_impl(win_iocp_operation* op, + DWORD last_error, size_t bytes_transferred) + { + std::auto_ptr > h( + static_cast*>(op)); + + // Map ERROR_NETNAME_DELETED to more useful error. + if (last_error == ERROR_NETNAME_DELETED) + { + if (h->socket_ptr_.expired()) + last_error = ERROR_OPERATION_ABORTED; + else + last_error = WSAECONNRESET; + } + + boost::asio::error error(last_error); + h->handler_(error, bytes_transferred); + } + + typename demuxer_type::work work_; + socket_weak_ptr_type socket_ptr_; + Handler handler_; + }; + + // Start an asynchronous send. The data being sent must be valid for the + // lifetime of the asynchronous operation. + template + void async_send(impl_type& impl, const Const_Buffers& buffers, + socket_base::message_flags flags, Handler handler) + { + std::auto_ptr > op( + new send_operation(demuxer_, impl.socket_, handler)); + + // Copy buffers into WSABUF array. + ::WSABUF bufs[max_buffers]; + typename Const_Buffers::const_iterator iter = buffers.begin(); + typename Const_Buffers::const_iterator end = buffers.end(); + DWORD i = 0; + for (; iter != end && i < max_buffers; ++iter, ++i) + { + bufs[i].len = static_cast(boost::asio::buffer_size(*iter)); + bufs[i].buf = const_cast( + boost::asio::buffer_cast(*iter)); + } + + // Send the data. + DWORD bytes_transferred = 0; + int result = ::WSASend(impl, bufs, i, + &bytes_transferred, flags, op.get(), 0); + DWORD last_error = ::WSAGetLastError(); + + // Check if the operation completed immediately. + if (result != 0 && last_error != WSA_IO_PENDING) + { + boost::asio::error error(last_error); + demuxer_service_.post(bind_handler(handler, error, bytes_transferred)); + } + else + { + op.release(); + } + } + + // Send a datagram to the specified endpoint. Returns the number of bytes + // sent. + template + size_t send_to(impl_type& impl, const Const_Buffers& buffers, + socket_base::message_flags flags, const Endpoint& destination, + Error_Handler error_handler) + { + // Copy buffers into WSABUF array. + ::WSABUF bufs[max_buffers]; + typename Const_Buffers::const_iterator iter = buffers.begin(); + typename Const_Buffers::const_iterator end = buffers.end(); + DWORD i = 0; + for (; iter != end && i < max_buffers; ++iter, ++i) + { + bufs[i].len = static_cast(boost::asio::buffer_size(*iter)); + bufs[i].buf = const_cast( + boost::asio::buffer_cast(*iter)); + } + + // Send the data. + DWORD bytes_transferred = 0; + int result = ::WSASendTo(impl, bufs, i, &bytes_transferred, flags, + destination.data(), destination.size(), 0, 0); + if (result != 0) + { + DWORD last_error = ::WSAGetLastError(); + error_handler(boost::asio::error(last_error)); + return 0; + } + + return bytes_transferred; + } + + template + class send_to_operation + : public win_iocp_operation + { + public: + send_to_operation(demuxer_type& demuxer, Handler handler) + : win_iocp_operation(&send_to_operation::do_completion_impl), + work_(demuxer), + handler_(handler) + { + } + + private: + static void do_completion_impl(win_iocp_operation* op, + DWORD last_error, size_t bytes_transferred) + { + std::auto_ptr > h( + static_cast*>(op)); + boost::asio::error error(last_error); + h->handler_(error, bytes_transferred); + } + + typename demuxer_type::work work_; + Handler handler_; + }; + + // Start an asynchronous send. The data being sent must be valid for the + // lifetime of the asynchronous operation. + template + void async_send_to(impl_type& impl, const Const_Buffers& buffers, + socket_base::message_flags flags, const Endpoint& destination, + Handler handler) + { + std::auto_ptr > op( + new send_to_operation(demuxer_, handler)); + + // Copy buffers into WSABUF array. + ::WSABUF bufs[max_buffers]; + typename Const_Buffers::const_iterator iter = buffers.begin(); + typename Const_Buffers::const_iterator end = buffers.end(); + DWORD i = 0; + for (; iter != end && i < max_buffers; ++iter, ++i) + { + bufs[i].len = static_cast(boost::asio::buffer_size(*iter)); + bufs[i].buf = const_cast( + boost::asio::buffer_cast(*iter)); + } + + // Send the data. + DWORD bytes_transferred = 0; + int result = ::WSASendTo(impl, bufs, i, &bytes_transferred, flags, + destination.data(), destination.size(), op.get(), 0); + DWORD last_error = ::WSAGetLastError(); + + // Check if the operation completed immediately. + if (result != 0 && last_error != WSA_IO_PENDING) + { + boost::asio::error error(last_error); + demuxer_service_.post(bind_handler(handler, error, bytes_transferred)); + } + else + { + op.release(); + } + } + + // Receive some data from the peer. Returns the number of bytes received or + // 0 if the connection was closed cleanly. + template + size_t receive(impl_type& impl, const Mutable_Buffers& buffers, + socket_base::message_flags flags, Error_Handler error_handler) + { + // Copy buffers into WSABUF array. + ::WSABUF bufs[max_buffers]; + typename Mutable_Buffers::const_iterator iter = buffers.begin(); + typename Mutable_Buffers::const_iterator end = buffers.end(); + DWORD i = 0; + for (; iter != end && i < max_buffers; ++iter, ++i) + { + bufs[i].len = static_cast(boost::asio::buffer_size(*iter)); + bufs[i].buf = boost::asio::buffer_cast(*iter); + } + + // Receive some data. + DWORD bytes_transferred = 0; + DWORD recv_flags = flags; + int result = ::WSARecv(impl, bufs, i, + &bytes_transferred, &recv_flags, 0, 0); + if (result != 0) + { + DWORD last_error = ::WSAGetLastError(); + if (last_error == ERROR_NETNAME_DELETED) + last_error = WSAECONNRESET; + error_handler(boost::asio::error(last_error)); + return 0; + } + if (bytes_transferred == 0) + { + error_handler(boost::asio::error(boost::asio::error::eof)); + return 0; + } + + return bytes_transferred; + } + + template + class receive_operation + : public win_iocp_operation + { + public: + receive_operation(demuxer_type& demuxer, + socket_weak_ptr_type socket_ptr, Handler handler) + : win_iocp_operation(&receive_operation::do_completion_impl), + work_(demuxer), + socket_ptr_(socket_ptr), + handler_(handler) + { + } + + private: + static void do_completion_impl(win_iocp_operation* op, + DWORD last_error, size_t bytes_transferred) + { + std::auto_ptr > h( + static_cast*>(op)); + + // Map ERROR_NETNAME_DELETED to more useful error. + if (last_error == ERROR_NETNAME_DELETED) + { + if (h->socket_ptr_.expired()) + last_error = ERROR_OPERATION_ABORTED; + else + last_error = WSAECONNRESET; + } + + // Check for connection closed. + else if (last_error == 0 && bytes_transferred == 0) + { + last_error = boost::asio::error::eof; + } + + boost::asio::error error(last_error); + h->handler_(error, bytes_transferred); + } + + typename demuxer_type::work work_; + socket_weak_ptr_type socket_ptr_; + Handler handler_; + }; + + // Start an asynchronous receive. The buffer for the data being received + // must be valid for the lifetime of the asynchronous operation. + template + void async_receive(impl_type& impl, const Mutable_Buffers& buffers, + socket_base::message_flags flags, Handler handler) + { + std::auto_ptr > op( + new receive_operation(demuxer_, impl.socket_, handler)); + + // Copy buffers into WSABUF array. + ::WSABUF bufs[max_buffers]; + typename Mutable_Buffers::const_iterator iter = buffers.begin(); + typename Mutable_Buffers::const_iterator end = buffers.end(); + DWORD i = 0; + for (; iter != end && i < max_buffers; ++iter, ++i) + { + bufs[i].len = static_cast(boost::asio::buffer_size(*iter)); + bufs[i].buf = boost::asio::buffer_cast(*iter); + } + + // Receive some data. + DWORD bytes_transferred = 0; + DWORD recv_flags = flags; + int result = ::WSARecv(impl, bufs, i, + &bytes_transferred, &recv_flags, op.get(), 0); + DWORD last_error = ::WSAGetLastError(); + if (result != 0 && last_error != WSA_IO_PENDING) + { + boost::asio::error error(last_error); + demuxer_service_.post(bind_handler(handler, error, bytes_transferred)); + } + else + { + op.release(); + } + } + + // Receive a datagram with the endpoint of the sender. Returns the number of + // bytes received. + template + size_t receive_from(impl_type& impl, const Mutable_Buffers& buffers, + socket_base::message_flags flags, Endpoint& sender_endpoint, + Error_Handler error_handler) + { + // Copy buffers into WSABUF array. + ::WSABUF bufs[max_buffers]; + typename Mutable_Buffers::const_iterator iter = buffers.begin(); + typename Mutable_Buffers::const_iterator end = buffers.end(); + DWORD i = 0; + for (; iter != end && i < max_buffers; ++iter, ++i) + { + bufs[i].len = static_cast(boost::asio::buffer_size(*iter)); + bufs[i].buf = boost::asio::buffer_cast(*iter); + } + + // Receive some data. + DWORD bytes_transferred = 0; + DWORD recv_flags = flags; + int endpoint_size = sender_endpoint.size(); + int result = ::WSARecvFrom(impl, bufs, i, &bytes_transferred, &recv_flags, + sender_endpoint.data(), &endpoint_size, 0, 0); + if (result != 0) + { + DWORD last_error = ::WSAGetLastError(); + error_handler(boost::asio::error(last_error)); + return 0; + } + if (bytes_transferred == 0) + { + error_handler(boost::asio::error(boost::asio::error::eof)); + return 0; + } + + sender_endpoint.size(endpoint_size); + + return bytes_transferred; + } + + template + class receive_from_operation + : public win_iocp_operation + { + public: + receive_from_operation(demuxer_type& demuxer, Endpoint& endpoint, + Handler handler) + : win_iocp_operation( + &receive_from_operation::do_completion_impl), + endpoint_(endpoint), + endpoint_size_(endpoint.size()), + work_(demuxer), + handler_(handler) + { + } + + int& endpoint_size() + { + return endpoint_size_; + } + + private: + static void do_completion_impl(win_iocp_operation* op, + DWORD last_error, size_t bytes_transferred) + { + std::auto_ptr > h( + static_cast*>(op)); + + // Check for connection closed. + if (last_error == 0 && bytes_transferred == 0) + { + last_error = boost::asio::error::eof; + } + + h->endpoint_.size(h->endpoint_size_); + boost::asio::error error(last_error); + h->handler_(error, bytes_transferred); + } + + Endpoint& endpoint_; + int endpoint_size_; + typename demuxer_type::work work_; + Handler handler_; + }; + + // Start an asynchronous receive. The buffer for the data being received and + // the sender_endpoint object must both be valid for the lifetime of the + // asynchronous operation. + template + void async_receive_from(impl_type& impl, const Mutable_Buffers& buffers, + socket_base::message_flags flags, Endpoint& sender_endp, Handler handler) + { + std::auto_ptr > op( + new receive_from_operation( + demuxer_, sender_endp, handler)); + + // Copy buffers into WSABUF array. + ::WSABUF bufs[max_buffers]; + typename Mutable_Buffers::const_iterator iter = buffers.begin(); + typename Mutable_Buffers::const_iterator end = buffers.end(); + DWORD i = 0; + for (; iter != end && i < max_buffers; ++iter, ++i) + { + bufs[i].len = static_cast(boost::asio::buffer_size(*iter)); + bufs[i].buf = boost::asio::buffer_cast(*iter); + } + + // Receive some data. + DWORD bytes_transferred = 0; + DWORD recv_flags = flags; + int result = ::WSARecvFrom(impl, bufs, i, &bytes_transferred, &recv_flags, + sender_endp.data(), &op->endpoint_size(), op.get(), 0); + DWORD last_error = ::WSAGetLastError(); + if (result != 0 && last_error != WSA_IO_PENDING) + { + boost::asio::error error(last_error); + demuxer_service_.post(bind_handler(handler, error, bytes_transferred)); + } + else + { + op.release(); + } + } + + // Accept a new connection. + template + void accept(impl_type& impl, Socket& peer, Error_Handler error_handler) + { + // We cannot accept a socket that is already open. + if (peer.impl() != invalid_socket) + { + error_handler(boost::asio::error(boost::asio::error::already_connected)); + return; + } + + impl_type new_socket(socket_ops::accept(impl, 0, 0)); + if (int err = socket_ops::get_error()) + { + error_handler(boost::asio::error(err)); + return; + } + + peer.set_impl(new_socket); + } + + // Accept a new connection. + template + void accept_endpoint(impl_type& impl, Socket& peer, Endpoint& peer_endpoint, + Error_Handler error_handler) + { + // We cannot accept a socket that is already open. + if (peer.impl() != invalid_socket) + { + error_handler(boost::asio::error(boost::asio::error::already_connected)); + return; + } + + socket_addr_len_type addr_len = peer_endpoint.size(); + impl_type new_socket(socket_ops::accept(impl, + peer_endpoint.data(), &addr_len)); + if (int err = socket_ops::get_error()) + { + error_handler(boost::asio::error(err)); + return; + } + + peer_endpoint.size(addr_len); + + peer.set_impl(new_socket); + } + + template + class accept_operation + : public win_iocp_operation + { + public: + accept_operation(demuxer_type& demuxer, impl_type& impl, + socket_type new_socket, Socket& peer, Handler handler) + : win_iocp_operation( + &accept_operation::do_completion_impl), + demuxer_(demuxer), + impl_(impl), + new_socket_(new_socket), + peer_(peer), + work_(demuxer), + handler_(handler) + { + } + + socket_type new_socket() + { + return new_socket_.get(); + } + + void* output_buffer() + { + return output_buffer_; + } + + DWORD address_length() + { + return sizeof(sockaddr_storage) + 16; + } + + private: + static void do_completion_impl(win_iocp_operation* op, + DWORD last_error, size_t bytes_transferred) + { + std::auto_ptr > h( + static_cast*>(op)); + + // Check for connection aborted. + if (last_error == ERROR_NETNAME_DELETED) + { + last_error = boost::asio::error::connection_aborted; + } + + // Check whether the operation was successful. + if (last_error != 0) + { + boost::asio::error error(last_error); + h->handler_(error); + return; + } + + // Need to set the SO_UPDATE_ACCEPT_CONTEXT option so that getsockname + // and getpeername will work on the accepted socket. + DWORD update_ctx_param = h->impl_; + if (socket_ops::setsockopt(h->new_socket_.get(), SOL_SOCKET, + SO_UPDATE_ACCEPT_CONTEXT, &update_ctx_param, sizeof(DWORD)) != 0) + { + boost::asio::error error(socket_ops::get_error()); + h->handler_(error); + return; + } + + // Socket was successfully connected. Transfer ownership of the socket to + // the peer object. + impl_type new_socket(h->new_socket_.get()); + h->peer_.set_impl(new_socket); + h->new_socket_.release(); + boost::asio::error error(boost::asio::error::success); + h->handler_(error); + } + + demuxer_type& demuxer_; + impl_type& impl_; + socket_holder new_socket_; + Socket& peer_; + typename demuxer_type::work work_; + unsigned char output_buffer_[(sizeof(sockaddr_storage) + 16) * 2]; + Handler handler_; + }; + + // Start an asynchronous accept. The peer object must be valid until the + // accept's handler is invoked. + template + void async_accept(impl_type& impl, Socket& peer, Handler handler) + { + // Check whether acceptor has been initialised. + if (impl == null()) + { + boost::asio::error error(boost::asio::error::bad_descriptor); + demuxer_.post(bind_handler(handler, error)); + return; + } + + // Check that peer socket has not already been connected. + if (peer.impl() != invalid_socket) + { + boost::asio::error error(boost::asio::error::already_connected); + demuxer_.post(bind_handler(handler, error)); + return; + } + + // Get information about the protocol used by the socket. + WSAPROTOCOL_INFO protocol_info; + std::size_t protocol_info_size = sizeof(protocol_info); + if (socket_ops::getsockopt(impl, SOL_SOCKET, SO_PROTOCOL_INFO, + &protocol_info, &protocol_info_size) != 0) + { + boost::asio::error error(socket_ops::get_error()); + demuxer_.post(bind_handler(handler, error)); + return; + } + + // Create a new socket for the connection. + socket_holder sock(socket_ops::socket(protocol_info.iAddressFamily, + protocol_info.iSocketType, protocol_info.iProtocol)); + if (sock.get() == invalid_socket) + { + boost::asio::error error(socket_ops::get_error()); + demuxer_.post(bind_handler(handler, error)); + return; + } + + // Create new operation object. Ownership of new socket is transferred. + std::auto_ptr > op( + new accept_operation( + demuxer_, impl, sock.get(), peer, handler)); + sock.release(); + + // Accept a connection. + DWORD bytes_read = 0; + BOOL result = ::AcceptEx(impl, op->new_socket(), op->output_buffer(), 0, + op->address_length(), op->address_length(), &bytes_read, op.get()); + DWORD last_error = ::WSAGetLastError(); + + // Check if the operation completed immediately. + if (!result && last_error != WSA_IO_PENDING) + { + boost::asio::error error(last_error); + demuxer_service_.post(bind_handler(handler, error)); + } + else + { + op.release(); + } + } + + template + class accept_endp_operation + : public win_iocp_operation + { + public: + accept_endp_operation(demuxer_type& demuxer, impl_type& impl, + socket_type new_socket, Socket& peer, Endpoint& peer_endpoint, + Handler handler) + : win_iocp_operation(&accept_endp_operation< + Socket, Endpoint, Handler>::do_completion_impl), + demuxer_(demuxer), + impl_(impl), + new_socket_(new_socket), + peer_(peer), + peer_endpoint_(peer_endpoint), + work_(demuxer), + handler_(handler) + { + } + + socket_type new_socket() + { + return new_socket_.get(); + } + + void* output_buffer() + { + return output_buffer_; + } + + DWORD address_length() + { + return sizeof(sockaddr_storage) + 16; + } + + private: + static void do_completion_impl(win_iocp_operation* op, + DWORD last_error, size_t bytes_transferred) + { + std::auto_ptr > h( + static_cast*>(op)); + + // Check for connection aborted. + if (last_error == ERROR_NETNAME_DELETED) + { + last_error = boost::asio::error::connection_aborted; + } + + // Check whether the operation was successful. + if (last_error != 0) + { + boost::asio::error error(last_error); + h->handler_(error); + return; + } + + // Get the address of the peer. + LPSOCKADDR local_addr = 0; + int local_addr_length = 0; + LPSOCKADDR remote_addr = 0; + int remote_addr_length = 0; + GetAcceptExSockaddrs(h->output_buffer(), 0, h->address_length(), + h->address_length(), &local_addr, &local_addr_length, &remote_addr, + &remote_addr_length); + if (remote_addr_length > h->peer_endpoint_.size()) + { + boost::asio::error error(boost::asio::error::invalid_argument); + h->handler_(error); + return; + } + h->peer_endpoint_.size(remote_addr_length); + using namespace std; // For memcpy. + memcpy(h->peer_endpoint_.data(), remote_addr, remote_addr_length); + + // Need to set the SO_UPDATE_ACCEPT_CONTEXT option so that getsockname + // and getpeername will work on the accepted socket. + DWORD update_ctx_param = h->impl_; + if (socket_ops::setsockopt(h->new_socket_.get(), SOL_SOCKET, + SO_UPDATE_ACCEPT_CONTEXT, &update_ctx_param, sizeof(DWORD)) != 0) + { + boost::asio::error error(socket_ops::get_error()); + h->handler_(error); + return; + } + + // Socket was successfully connected. Transfer ownership of the socket to + // the peer object. + impl_type new_socket(h->new_socket_.get()); + h->peer_.set_impl(new_socket); + h->new_socket_.release(); + boost::asio::error error(boost::asio::error::success); + h->handler_(error); + } + + demuxer_type& demuxer_; + impl_type& impl_; + socket_holder new_socket_; + Socket& peer_; + Endpoint& peer_endpoint_; + typename demuxer_type::work work_; + unsigned char output_buffer_[(sizeof(sockaddr_storage) + 16) * 2]; + Handler handler_; + }; + + // Start an asynchronous accept. The peer and peer_endpoint objects + // must be valid until the accept's handler is invoked. + template + void async_accept_endpoint(impl_type& impl, Socket& peer, + Endpoint& peer_endpoint, Handler handler) + { + // Check whether acceptor has been initialised. + if (impl == null()) + { + boost::asio::error error(boost::asio::error::bad_descriptor); + demuxer_.post(bind_handler(handler, error)); + return; + } + + // Check that peer socket has not already been connected. + if (peer.impl() != invalid_socket) + { + boost::asio::error error(boost::asio::error::already_connected); + demuxer_.post(bind_handler(handler, error)); + return; + } + + // Get information about the protocol used by the socket. + WSAPROTOCOL_INFO protocol_info; + std::size_t protocol_info_size = sizeof(protocol_info); + if (socket_ops::getsockopt(impl, SOL_SOCKET, SO_PROTOCOL_INFO, + &protocol_info, &protocol_info_size) != 0) + { + boost::asio::error error(socket_ops::get_error()); + demuxer_.post(bind_handler(handler, error)); + return; + } + + // Create a new socket for the connection. + socket_holder sock(socket_ops::socket(protocol_info.iAddressFamily, + protocol_info.iSocketType, protocol_info.iProtocol)); + if (sock.get() == invalid_socket) + { + boost::asio::error error(socket_ops::get_error()); + demuxer_.post(bind_handler(handler, error)); + return; + } + + // Create new operation object. Ownership of new socket is transferred. + std::auto_ptr > op( + new accept_endp_operation( + demuxer_, impl, sock.get(), peer, peer_endpoint, handler)); + sock.release(); + + // Accept a connection. + DWORD bytes_read = 0; + BOOL result = ::AcceptEx(impl, op->new_socket(), op->output_buffer(), 0, + op->address_length(), op->address_length(), &bytes_read, op.get()); + DWORD last_error = ::WSAGetLastError(); + + // Check if the operation completed immediately. + if (!result && last_error != WSA_IO_PENDING) + { + boost::asio::error error(last_error); + demuxer_service_.post(bind_handler(handler, error)); + } + else + { + op.release(); + } + } + + // Connect the socket to the specified endpoint. + template + void connect(impl_type& impl, const Endpoint& peer_endpoint, + Error_Handler error_handler) + { + // Open the socket if it is not already open. + if (impl == invalid_socket) + { + // Get the flags used to create the new socket. + int family = peer_endpoint.protocol().family(); + int type = peer_endpoint.protocol().type(); + int proto = peer_endpoint.protocol().protocol(); + + // Create a new socket. + impl = socket_ops::socket(family, type, proto); + if (impl == invalid_socket) + { + error_handler(boost::asio::error(socket_ops::get_error())); + return; + } + demuxer_service_.register_socket(impl); + } + + // Perform the connect operation. + int result = socket_ops::connect(impl, peer_endpoint.data(), + peer_endpoint.size()); + if (result == socket_error_retval) + error_handler(boost::asio::error(socket_ops::get_error())); + } + + template + class connect_handler + { + public: + connect_handler(impl_type& impl, boost::shared_ptr completed, + demuxer_type& demuxer, reactor_type& reactor, Handler handler) + : impl_(impl), + completed_(completed), + demuxer_(demuxer), + reactor_(reactor), + work_(demuxer), + handler_(handler) + { + } + + void operator()(int result) + { + // Check whether a handler has already been called for the connection. + // If it has, then we don't want to do anything in this handler. + if (*completed_) + return; + + // Cancel the other reactor operation for the connection. + *completed_ = true; + reactor_.enqueue_cancel_ops_unlocked(impl_); + + // Check whether the operation was successful. + if (result != 0) + { + boost::asio::error error(result); + demuxer_.post(bind_handler(handler_, error)); + return; + } + + // Get the error code from the connect operation. + int connect_error = 0; + size_t connect_error_len = sizeof(connect_error); + if (socket_ops::getsockopt(impl_, SOL_SOCKET, SO_ERROR, + &connect_error, &connect_error_len) == socket_error_retval) + { + boost::asio::error error(socket_ops::get_error()); + demuxer_.post(bind_handler(handler_, error)); + return; + } + + // If connection failed then post the handler with the error code. + if (connect_error) + { + boost::asio::error error(connect_error); + demuxer_.post(bind_handler(handler_, error)); + return; + } + + // Make the socket blocking again (the default). + ioctl_arg_type non_blocking = 0; + if (socket_ops::ioctl(impl_, FIONBIO, &non_blocking)) + { + boost::asio::error error(socket_ops::get_error()); + demuxer_.post(bind_handler(handler_, error)); + return; + } + + // Post the result of the successful connection operation. + boost::asio::error error(boost::asio::error::success); + demuxer_.post(bind_handler(handler_, error)); + } + + private: + impl_type& impl_; + boost::shared_ptr completed_; + demuxer_type& demuxer_; + reactor_type& reactor_; + typename demuxer_type::work work_; + Handler handler_; + }; + + // Start an asynchronous connect. + template + void async_connect(impl_type& impl, const Endpoint& peer_endpoint, + Handler handler) + { + // Open the socket if it is not already open. + if (impl == invalid_socket) + { + // Get the flags used to create the new socket. + int family = peer_endpoint.protocol().family(); + int type = peer_endpoint.protocol().type(); + int proto = peer_endpoint.protocol().protocol(); + + // Create a new socket. + impl = socket_ops::socket(family, type, proto); + if (impl == invalid_socket) + { + boost::asio::error error(socket_ops::get_error()); + demuxer_.post(bind_handler(handler, error)); + return; + } + demuxer_service_.register_socket(impl); + } + + // Mark the socket as non-blocking so that the connection will take place + // asynchronously. + ioctl_arg_type non_blocking = 1; + if (socket_ops::ioctl(impl, FIONBIO, &non_blocking)) + { + boost::asio::error error(socket_ops::get_error()); + demuxer_.post(bind_handler(handler, error)); + return; + } + + // Start the connect operation. + if (socket_ops::connect(impl, peer_endpoint.data(), + peer_endpoint.size()) == 0) + { + // The connect operation has finished successfully so we need to post the + // handler immediately. + boost::asio::error error(boost::asio::error::success); + demuxer_.post(bind_handler(handler, error)); + } + else if (socket_ops::get_error() == boost::asio::error::in_progress + || socket_ops::get_error() == boost::asio::error::would_block) + { + // The connection is happening in the background, and we need to wait + // until the socket becomes writeable. + boost::shared_ptr completed(new bool(false)); + reactor_.start_write_and_except_ops(impl, connect_handler( + impl, completed, demuxer_, reactor_, handler)); + } + else + { + // The connect operation has failed, so post the handler immediately. + boost::asio::error error(socket_ops::get_error()); + demuxer_.post(bind_handler(handler, error)); + } + } + +private: + // The demuxer associated with the service. + demuxer_type& demuxer_; + + // The demuxer service used for running asynchronous operations and + // dispatching handlers. + win_iocp_demuxer_service& demuxer_service_; + + // The reactor used for performing accept and connect operations. + reactor_type& reactor_; +}; + +} // namespace detail +} // namespace asio +} // namespace boost + +#endif // defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0400) +#endif // defined(BOOST_WINDOWS) + +#include + +#endif // BOOST_ASIO_DETAIL_WIN_IOCP_SOCKET_SERVICE_HPP diff --git a/include/boost/asio/detail/win_local_free_on_block_exit.hpp b/include/boost/asio/detail/win_local_free_on_block_exit.hpp new file mode 100644 index 00000000..f0ecb2b2 --- /dev/null +++ b/include/boost/asio/detail/win_local_free_on_block_exit.hpp @@ -0,0 +1,61 @@ +// +// win_local_free_on_block_exit.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2005 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_DETAIL_WIN_LOCAL_FREE_ON_BLOCK_EXIT_HPP +#define BOOST_ASIO_DETAIL_WIN_LOCAL_FREE_ON_BLOCK_EXIT_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include + +#include +#include +#include + +#if defined(BOOST_WINDOWS) + +#include +#include + +namespace boost { +namespace asio { +namespace detail { + +class win_local_free_on_block_exit + : private noncopyable +{ +public: + // Constructor blocks all signals for the calling thread. + explicit win_local_free_on_block_exit(void* p) + : p_(p) + { + } + + // Destructor restores the previous signal mask. + ~win_local_free_on_block_exit() + { + ::LocalFree(p_); + } + +private: + void* p_; +}; + +} // namespace detail +} // namespace asio +} // namespace boost + +#endif // defined(BOOST_WINDOWS) + +#include + +#endif // BOOST_ASIO_DETAIL_WIN_LOCAL_FREE_ON_BLOCK_EXIT_HPP diff --git a/include/boost/asio/detail/win_mutex.hpp b/include/boost/asio/detail/win_mutex.hpp new file mode 100644 index 00000000..437170b0 --- /dev/null +++ b/include/boost/asio/detail/win_mutex.hpp @@ -0,0 +1,144 @@ +// +// win_mutex.hpp +// ~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2005 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_DETAIL_WIN_MUTEX_HPP +#define BOOST_ASIO_DETAIL_WIN_MUTEX_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include + +#include +#include +#include + +#if defined(BOOST_WINDOWS) + +#include +#include +#include +#include + +#include +#include +#include + +namespace boost { +namespace asio { +namespace detail { + +class win_mutex + : private noncopyable +{ +public: + typedef boost::asio::detail::scoped_lock scoped_lock; + + // Constructor. + win_mutex() + { + int error = do_init(); + if (error != 0) + { + system_exception e("mutex", error); + boost::throw_exception(e); + } + } + + // Destructor. + ~win_mutex() + { + ::DeleteCriticalSection(&crit_section_); + } + + // Lock the mutex. + void lock() + { + int error = do_lock(); + if (error != 0) + { + system_exception e("mutex", error); + boost::throw_exception(e); + } + } + + // Unlock the mutex. + void unlock() + { + ::LeaveCriticalSection(&crit_section_); + } + +private: + // Initialisation must be performed in a separate function to the constructor + // since the compiler does not support the use of structured exceptions and + // C++ exceptions in the same function. + int do_init() + { +#if defined(__MINGW32__) + // Not sure if MinGW supports structured exception handling, so for now + // we'll just call the Windows API and hope. + ::InitializeCriticalSection(&crit_section_); + return 0; +#else + __try + { + ::InitializeCriticalSection(&crit_section_); + } + __except(GetExceptionCode() == STATUS_NO_MEMORY + ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) + { + return ERROR_OUTOFMEMORY; + } + + return 0; +#endif + } + + // Locking must be performed in a separate function to lock() since the + // compiler does not support the use of structured exceptions and C++ + // exceptions in the same function. + int do_lock() + { +#if defined(__MINGW32__) + // Not sure if MinGW supports structured exception handling, so for now + // we'll just call the Windows API and hope. + ::EnterCriticalSection(&crit_section_); + return 0; +#else + __try + { + ::EnterCriticalSection(&crit_section_); + } + __except(GetExceptionCode() == STATUS_INVALID_HANDLE + || GetExceptionCode() == STATUS_NO_MEMORY + ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) + { + if (GetExceptionCode() == STATUS_NO_MEMORY) + return ERROR_OUTOFMEMORY; + return ERROR_INVALID_HANDLE; + } + + return 0; +#endif + } + + ::CRITICAL_SECTION crit_section_; +}; + +} // namespace detail +} // namespace asio +} // namespace boost + +#endif // defined(BOOST_WINDOWS) + +#include + +#endif // BOOST_ASIO_DETAIL_WIN_MUTEX_HPP diff --git a/include/boost/asio/detail/win_signal_blocker.hpp b/include/boost/asio/detail/win_signal_blocker.hpp new file mode 100644 index 00000000..433424b6 --- /dev/null +++ b/include/boost/asio/detail/win_signal_blocker.hpp @@ -0,0 +1,69 @@ +// +// win_signal_blocker.hpp +// ~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2005 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_DETAIL_WIN_SIGNAL_BLOCKER_HPP +#define BOOST_ASIO_DETAIL_WIN_SIGNAL_BLOCKER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include + +#include +#include +#include + +#if defined(BOOST_WINDOWS) + +#include + +namespace boost { +namespace asio { +namespace detail { + +class win_signal_blocker + : private noncopyable +{ +public: + // Constructor blocks all signals for the calling thread. + win_signal_blocker() + { + // No-op. + } + + // Destructor restores the previous signal mask. + ~win_signal_blocker() + { + // No-op. + } + + // Block all signals for the calling thread. + void block() + { + // No-op. + } + + // Restore the previous signal mask. + void unblock() + { + // No-op. + } +}; + +} // namespace detail +} // namespace asio +} // namespace boost + +#endif // defined(BOOST_WINDOWS) + +#include + +#endif // BOOST_ASIO_DETAIL_WIN_SIGNAL_BLOCKER_HPP diff --git a/include/boost/asio/detail/win_thread.hpp b/include/boost/asio/detail/win_thread.hpp new file mode 100644 index 00000000..5b48f5f9 --- /dev/null +++ b/include/boost/asio/detail/win_thread.hpp @@ -0,0 +1,123 @@ +// +// win_thread.hpp +// ~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2005 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_DETAIL_WIN_THREAD_HPP +#define BOOST_ASIO_DETAIL_WIN_THREAD_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include + +#include +#include +#include + +#if defined(BOOST_WINDOWS) + +#include +#include +#include + +#include +#include +#include +#include +#include + +namespace boost { +namespace asio { +namespace detail { + +unsigned int __stdcall win_thread_function(void* arg); + +class win_thread + : private noncopyable +{ +public: + // Constructor. + template + win_thread(Function f) + { + std::auto_ptr arg(new func(f)); + unsigned int thread_id = 0; + thread_ = reinterpret_cast(::_beginthreadex(0, 0, + win_thread_function, arg.get(), 0, &thread_id)); + if (!thread_) + { + DWORD last_error = ::GetLastError(); + system_exception e("thread", last_error); + boost::throw_exception(e); + } + arg.release(); + } + + // Destructor. + ~win_thread() + { + ::CloseHandle(thread_); + } + + // Wait for the thread to exit. + void join() + { + ::WaitForSingleObject(thread_, INFINITE); + } + +private: + friend unsigned int __stdcall win_thread_function(void* arg); + + class func_base + { + public: + virtual ~func_base() {} + virtual void run() = 0; + }; + + template + class func + : public func_base + { + public: + func(Function f) + : f_(f) + { + } + + virtual void run() + { + f_(); + } + + private: + Function f_; + }; + + ::HANDLE thread_; +}; + +inline unsigned int __stdcall win_thread_function(void* arg) +{ + std::auto_ptr func( + static_cast(arg)); + func->run(); + return 0; +} + +} // namespace detail +} // namespace asio +} // namespace boost + +#endif // defined(BOOST_WINDOWS) + +#include + +#endif // BOOST_ASIO_DETAIL_WIN_THREAD_HPP diff --git a/include/boost/asio/detail/win_tss_ptr.hpp b/include/boost/asio/detail/win_tss_ptr.hpp new file mode 100644 index 00000000..5cac4884 --- /dev/null +++ b/include/boost/asio/detail/win_tss_ptr.hpp @@ -0,0 +1,87 @@ +// +// win_tss_ptr.hpp +// ~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2005 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_DETAIL_WIN_TSS_PTR_HPP +#define BOOST_ASIO_DETAIL_WIN_TSS_PTR_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include + +#include +#include +#include + +#if defined(BOOST_WINDOWS) + +#include +#include +#include + +#include +#include +#include + +namespace boost { +namespace asio { +namespace detail { + +template +class win_tss_ptr + : private noncopyable +{ +public: + // Constructor. + win_tss_ptr() + { + tss_key_ = ::TlsAlloc(); + if (tss_key_ == TLS_OUT_OF_INDEXES) + { + DWORD last_error = ::GetLastError(); + system_exception e("tss", last_error); + boost::throw_exception(e); + } + } + + // Destructor. + ~win_tss_ptr() + { + ::TlsFree(tss_key_); + } + + // Get the value. + operator T*() const + { + return static_cast(::TlsGetValue(tss_key_)); + } + + // Set the value. + void operator=(T* value) + { + ::TlsSetValue(tss_key_, value); + } + +private: + // Thread-specific storage to allow unlocked access to determine whether a + // thread is a member of the pool. + DWORD tss_key_; +}; + +} // namespace detail +} // namespace asio +} // namespace boost + +#endif // defined(BOOST_WINDOWS) + +#include + +#endif // BOOST_ASIO_DETAIL_WIN_TSS_PTR_HPP diff --git a/include/boost/asio/detail/winsock_init.hpp b/include/boost/asio/detail/winsock_init.hpp new file mode 100644 index 00000000..53060603 --- /dev/null +++ b/include/boost/asio/detail/winsock_init.hpp @@ -0,0 +1,118 @@ +// +// winsock_init.hpp +// ~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2005 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_DETAIL_WINSOCK_INIT_HPP +#define BOOST_ASIO_DETAIL_WINSOCK_INIT_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include + +#include +#include +#include + +#if defined(BOOST_WINDOWS) + +#include +#include +#include +#include + +#include +#include +#include + +namespace boost { +namespace asio { +namespace detail { + +template +class winsock_init + : private noncopyable +{ +private: + // Structure to perform the actual initialisation. + struct do_init + { + do_init() + { + WSADATA wsa_data; + result_ = ::WSAStartup(MAKEWORD(Major, Minor), &wsa_data); + } + + ~do_init() + { + ::WSACleanup(); + } + + int result() const + { + return result_; + } + + // Helper function to manage a do_init singleton. The static instance of the + // winsock_init object ensures that this function is always called before + // main, and therefore before any other threads can get started. The do_init + // instance must be static in this function to ensure that it gets + // initialised before any other global objects try to use it. + static boost::shared_ptr instance() + { + static boost::shared_ptr init(new do_init); + return init; + } + + private: + int result_; + }; + +public: + // Constructor. + winsock_init() + : ref_(do_init::instance()) + { + // Check whether winsock was successfully initialised. This check is not + // performed for the global instance since there will be nobody around to + // catch the exception. + if (this != &instance_ && ref_->result() != 0) + { + system_exception e("winsock", ref_->result()); + boost::throw_exception(e); + } + } + + // Destructor. + ~winsock_init() + { + } + +private: + // Instance to force initialisation of winsock at global scope. + static winsock_init instance_; + + // Reference to singleton do_init object to ensure that winsock does not get + // cleaned up until the last user has finished with it. + boost::shared_ptr ref_; +}; + +template +winsock_init winsock_init::instance_; + +} // namespace detail +} // namespace asio +} // namespace boost + +#endif // defined(BOOST_WINDOWS) + +#include + +#endif // BOOST_ASIO_DETAIL_WINSOCK_INIT_HPP diff --git a/include/boost/asio/detail/wrapped_handler.hpp b/include/boost/asio/detail/wrapped_handler.hpp new file mode 100644 index 00000000..e461b737 --- /dev/null +++ b/include/boost/asio/detail/wrapped_handler.hpp @@ -0,0 +1,125 @@ +// +// wrapped_handler.hpp +// ~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2005 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_DETAIL_WRAPPED_HANDLER_HPP +#define BOOST_ASIO_DETAIL_WRAPPED_HANDLER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include + +#include + +namespace boost { +namespace asio { +namespace detail { + +template +class wrapped_handler +{ +public: + typedef void result_type; + + wrapped_handler(Dispatcher& dispatcher, Handler handler) + : dispatcher_(dispatcher), + handler_(handler) + { + } + + void operator()() + { + dispatcher_.dispatch(handler_); + } + + void operator()() const + { + dispatcher_.dispatch(handler_); + } + + template + void operator()(Arg1 arg1) + { + dispatcher_.dispatch(detail::bind_handler(handler_, arg1)); + } + + template + void operator()(Arg1 arg1) const + { + dispatcher_.dispatch(detail::bind_handler(handler_, arg1)); + } + + template + void operator()(Arg1 arg1, Arg2 arg2) + { + dispatcher_.dispatch(detail::bind_handler(handler_, arg1, arg2)); + } + + template + void operator()(Arg1 arg1, Arg2 arg2) const + { + dispatcher_.dispatch(detail::bind_handler(handler_, arg1, arg2)); + } + + template + void operator()(Arg1 arg1, Arg2 arg2, Arg3 arg3) + { + dispatcher_.dispatch(detail::bind_handler(handler_, arg1, arg2, arg3)); + } + + template + void operator()(Arg1 arg1, Arg2 arg2, Arg3 arg3) const + { + dispatcher_.dispatch(detail::bind_handler(handler_, arg1, arg2, arg3)); + } + + template + void operator()(Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4) + { + dispatcher_.dispatch( + detail::bind_handler(handler_, arg1, arg2, arg3, arg4)); + } + + template + void operator()(Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4) const + { + dispatcher_.dispatch( + detail::bind_handler(handler_, arg1, arg2, arg3, arg4)); + } + + template + void operator()(Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4, Arg5 arg5) + { + dispatcher_.dispatch( + detail::bind_handler(handler_, arg1, arg2, arg3, arg4, arg5)); + } + + template + void operator()(Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4, Arg5 arg5) const + { + dispatcher_.dispatch( + detail::bind_handler(handler_, arg1, arg2, arg3, arg4, arg5)); + } + +private: + Dispatcher& dispatcher_; + Handler handler_; +}; + +} // namespace detail +} // namespace asio +} // namespace boost + +#include + +#endif // BOOST_ASIO_DETAIL_WRAPPED_HANDLER_HPP diff --git a/include/boost/asio/error.hpp b/include/boost/asio/error.hpp new file mode 100644 index 00000000..937285d7 --- /dev/null +++ b/include/boost/asio/error.hpp @@ -0,0 +1,358 @@ +// +// error.hpp +// ~~~~~~~~~ +// +// Copyright (c) 2003-2005 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_ERROR_HPP +#define BOOST_ASIO_ERROR_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#if BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564)) +# include +#endif // BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564)) +#include + +#include +#include + +namespace boost { +namespace asio { + +#if defined(BOOST_WINDOWS) +# define BOOST_ASIO_SOCKET_ERROR(e) WSA ## e +# define BOOST_ASIO_NETDB_ERROR(e) WSA ## e +# define BOOST_ASIO_OS_ERROR(e_win, e_posix) e_win +#else +# define BOOST_ASIO_SOCKET_ERROR(e) e +# define BOOST_ASIO_NETDB_ERROR(e) 16384 + e +# define BOOST_ASIO_OS_ERROR(e_win, e_posix) e_posix +#endif + +/// The error class is used to encapsulate system error codes. +class error + : public std::exception +{ +public: + /// Error codes. + enum code_type + { + /// Permission denied. + access_denied = BOOST_ASIO_SOCKET_ERROR(EACCES), + + /// Address family not supported by protocol. + address_family_not_supported = BOOST_ASIO_SOCKET_ERROR(EAFNOSUPPORT), + + /// Address already in use. + address_in_use = BOOST_ASIO_SOCKET_ERROR(EADDRINUSE), + + /// Transport endpoint is already connected. + already_connected = BOOST_ASIO_SOCKET_ERROR(EISCONN), + + /// Operation already in progress. + already_started = BOOST_ASIO_SOCKET_ERROR(EALREADY), + + /// A connection has been aborted. + connection_aborted = BOOST_ASIO_SOCKET_ERROR(ECONNABORTED), + + /// Connection refused. + connection_refused = BOOST_ASIO_SOCKET_ERROR(ECONNREFUSED), + + /// Connection reset by peer. + connection_reset = BOOST_ASIO_SOCKET_ERROR(ECONNRESET), + + /// Bad file descriptor. + bad_descriptor = BOOST_ASIO_SOCKET_ERROR(EBADF), + + /// End of file or stream. + eof = BOOST_ASIO_OS_ERROR(ERROR_HANDLE_EOF, -1), + + /// Bad address. + fault = BOOST_ASIO_SOCKET_ERROR(EFAULT), + + /// Host not found (authoritative). + host_not_found = BOOST_ASIO_NETDB_ERROR(HOST_NOT_FOUND), + + /// Host not found (non-authoritative). + host_not_found_try_again = BOOST_ASIO_NETDB_ERROR(TRY_AGAIN), + + /// No route to host. + host_unreachable = BOOST_ASIO_SOCKET_ERROR(EHOSTUNREACH), + + /// Operation now in progress. + in_progress = BOOST_ASIO_SOCKET_ERROR(EINPROGRESS), + + /// Interrupted system call. + interrupted = BOOST_ASIO_SOCKET_ERROR(EINTR), + + /// Invalid argument. + invalid_argument = BOOST_ASIO_SOCKET_ERROR(EINVAL), + + /// Message too long. + message_size = BOOST_ASIO_SOCKET_ERROR(EMSGSIZE), + + /// Network is down. + network_down = BOOST_ASIO_SOCKET_ERROR(ENETDOWN), + + /// Network dropped connection on reset. + network_reset = BOOST_ASIO_SOCKET_ERROR(ENETRESET), + + /// Network is unreachable. + network_unreachable = BOOST_ASIO_SOCKET_ERROR(ENETUNREACH), + + /// Too many open files. + no_descriptors = BOOST_ASIO_SOCKET_ERROR(EMFILE), + + /// No buffer space available. + no_buffer_space = BOOST_ASIO_SOCKET_ERROR(ENOBUFS), + + /// The host is valid but does not have address data. + no_host_data = BOOST_ASIO_NETDB_ERROR(NO_DATA), + + /// Cannot allocate memory. + no_memory = BOOST_ASIO_OS_ERROR(ERROR_OUTOFMEMORY, ENOMEM), + + /// Operation not permitted. + no_permission = BOOST_ASIO_OS_ERROR(ERROR_ACCESS_DENIED, EPERM), + + /// Protocol not available. + no_protocol_option = BOOST_ASIO_SOCKET_ERROR(ENOPROTOOPT), + + /// A non-recoverable error occurred. + no_recovery = BOOST_ASIO_NETDB_ERROR(NO_RECOVERY), + + /// Transport endpoint is not connected. + not_connected = BOOST_ASIO_SOCKET_ERROR(ENOTCONN), + + /// Socket operation on non-socket. + not_socket = BOOST_ASIO_SOCKET_ERROR(ENOTSOCK), + + /// Operation not supported. + not_supported = BOOST_ASIO_SOCKET_ERROR(EOPNOTSUPP), + + /// Operation cancelled. + operation_aborted = BOOST_ASIO_OS_ERROR(ERROR_OPERATION_ABORTED, ECANCELED), + + /// Cannot send after transport endpoint shutdown. + shut_down = BOOST_ASIO_SOCKET_ERROR(ESHUTDOWN), + + /// Success. + success = 0, + + /// Connection timed out. + timed_out = BOOST_ASIO_SOCKET_ERROR(ETIMEDOUT), + + /// Resource temporarily unavailable. + try_again = BOOST_ASIO_OS_ERROR(ERROR_RETRY, EAGAIN), + + /// The socket is marked non-blocking and the requested operation would + /// block. + would_block = BOOST_ASIO_SOCKET_ERROR(EWOULDBLOCK) + }; + + /// Default constructor. + error() + : code_(success) + { + } + + /// Construct with a specific error code. + error(int code) + : code_(code) + { + } + + /// Copy constructor. + error(const error& e) + : code_(e.code_) + { + } + + /// Destructor. + virtual ~error() throw () + { + } + + /// Assignment operator. + error& operator=(const error& e) + { + code_ = e.code_; + what_.reset(); + return *this; + } + + /// Get a string representation of the exception. + virtual const char* what() const throw () + { +#if defined(BOOST_WINDOWS) + try + { + if (!what_) + { + char* msg = 0; + DWORD length = ::FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER + | FORMAT_MESSAGE_FROM_SYSTEM + | FORMAT_MESSAGE_IGNORE_INSERTS, 0, code_, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (char*)&msg, 0, 0); + detail::win_local_free_on_block_exit local_free_obj(msg); + if (length && msg[length - 1] == '\n') + msg[--length] = '\0'; + if (length && msg[length - 1] == '\r') + msg[--length] = '\0'; + if (length) + what_.reset(new std::string(msg)); + else + return "asio error"; + } + return what_->c_str(); + } + catch (std::exception&) + { + return "asio error"; + } +#else // defined(BOOST_WINDOWS) + switch (code_) + { + case error::eof: + return "End of file."; + case error::host_not_found: + return "Host not found (authoritative)."; + case error::host_not_found_try_again: + return "Host not found (non-authoritative), try again later."; + case error::no_recovery: + return "A non-recoverable error occurred during database lookup."; + case error::no_host_data: + return "The name is valid, but it does not have associated data."; +#if !defined(__sun) + case error::operation_aborted: + return "Operation aborted."; +#endif // !defined(__sun) + default: +#if defined(__sun) + return strerror(code_); +#elif defined(__MACH__) && defined(__APPLE__) + try + { + char buf[256] = ""; + strerror_r(code_, buf, sizeof(buf)); + what_.reset(new std::string(buf)); + return what_->c_str(); + } + catch (std::exception&) + { + return "asio error"; + } +#else + try + { + char buf[256] = ""; + what_.reset(new std::string(strerror_r(code_, buf, sizeof(buf)))); + return what_->c_str(); + } + catch (std::exception&) + { + return "asio error"; + } +#endif + } +#endif // defined(BOOST_WINDOWS) + } + + /// Get the code associated with the error. + int code() const + { + return code_; + } + + struct unspecified_bool_type_t; + typedef unspecified_bool_type_t* unspecified_bool_type; + + /// Operator returns non-null if there is a non-success error code. + operator unspecified_bool_type() const + { + if (code_ == success) + return 0; + else + return reinterpret_cast(1); + } + + /// Operator to test if the error represents success. + bool operator!() const + { + return code_ == success; + } + + /// Equality operator to compare two error objects. + friend bool operator==(const error& e1, const error& e2) + { + return e1.code_ == e2.code_; + } + + /// Inequality operator to compare two error objects. + friend bool operator!=(const error& e1, const error& e2) + { + return e1.code_ != e2.code_; + } + +private: + // The code associated with the error. + int code_; + + // The string representation of the error. + mutable boost::scoped_ptr what_; +}; + +/// Output the string associated with an error. +/** + * Used to output a human-readable string that is associated with an error. + * + * @param os The output stream to which the string will be written. + * + * @param e The error to be written. + * + * @return The output stream. + * + * @relates boost::asio::error + */ +#if BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564)) +std::ostream& operator<<(std::ostream& os, const error& e) +{ + os << e.what(); + return os; +} +#else // BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564)) +template +Ostream& operator<<(Ostream& os, const error& e) +{ + os << e.what(); + return os; +} +#endif // BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564)) + +} // namespace asio +} // namespace boost + +#undef BOOST_ASIO_SOCKET_ERROR +#undef BOOST_ASIO_NETDB_ERROR +#undef BOOST_ASIO_OS_ERROR + +#include + +#endif // BOOST_ASIO_ERROR_HPP diff --git a/include/boost/asio/error_handler.hpp b/include/boost/asio/error_handler.hpp new file mode 100644 index 00000000..82f4fcaa --- /dev/null +++ b/include/boost/asio/error_handler.hpp @@ -0,0 +1,121 @@ +// +// error_handler.hpp +// ~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2005 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_ERROR_HANDLER_HPP +#define BOOST_ASIO_ERROR_HANDLER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include + +#include +#include +#include + +namespace boost { +namespace asio { + +namespace detail { + +class ignore_error_t +{ +public: + typedef void result_type; + + template + void operator()(const Error& err) const + { + } +}; + +class throw_error_t +{ +public: + typedef void result_type; + + template + void operator()(const Error& err) const + { + boost::throw_exception(err); + } +}; + +template +class assign_error_t +{ +public: + typedef void result_type; + + assign_error_t(Target& target) + : target_(&target) + { + } + + template + void operator()(const Error& err) const + { + *target_ = err; + } + +private: + Target* target_; +}; + +} // namespace detail + +/** + * @defgroup error_handler Error Handler Function Objects + * + * Function objects for custom error handling. + */ +/*@{*/ + +/// Return a function object that always ignores the error. +#if defined(GENERATING_DOCUMENTATION) +unspecified ignore_error(); +#else +inline detail::ignore_error_t ignore_error() +{ + return detail::ignore_error_t(); +} +#endif + +/// Return a function object that always throws the error. +#if defined(GENERATING_DOCUMENTATION) +unspecified throw_error(); +#else +inline detail::throw_error_t throw_error() +{ + return detail::throw_error_t(); +} +#endif + +/// Return a function object that assigns the error to a variable. +#if defined(GENERATING_DOCUMENTATION) +template +unspecified assign_error(Target& target); +#else +template +inline detail::assign_error_t assign_error(Target& target) +{ + return detail::assign_error_t(target); +} +#endif + +/*@}*/ + +} // namespace asio +} // namespace boost + +#include + +#endif // BOOST_ASIO_ERROR_HANDLER_HPP diff --git a/include/boost/asio/impl/read.ipp b/include/boost/asio/impl/read.ipp new file mode 100644 index 00000000..99dc98af --- /dev/null +++ b/include/boost/asio/impl/read.ipp @@ -0,0 +1,134 @@ +// +// read.ipp +// ~~~~~~~~ +// +// Copyright (c) 2003-2005 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_READ_IPP +#define BOOST_ASIO_READ_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include + +#include +#include +#include +#include + +namespace boost { +namespace asio { + +template +std::size_t read(Sync_Read_Stream& s, const Mutable_Buffers& buffers, + Completion_Condition completion_condition, Error_Handler error_handler) +{ + boost::asio::detail::consuming_buffers tmp(buffers); + std::size_t total_transferred = 0; + while (tmp.begin() != tmp.end()) + { + typename Sync_Read_Stream::error_type e; + std::size_t bytes_transferred = s.read_some(tmp, assign_error(e)); + tmp.consume(bytes_transferred); + total_transferred += bytes_transferred; + if (completion_condition(e, total_transferred)) + { + if (e) + { + error_handler(e); + } + return total_transferred; + } + } + return total_transferred; +} + +template +inline std::size_t read(Sync_Read_Stream& s, const Mutable_Buffers& buffers) +{ + return read(s, buffers, transfer_all(), throw_error()); +} + +template +inline std::size_t read(Sync_Read_Stream& s, const Mutable_Buffers& buffers, + Completion_Condition completion_condition) +{ + return read(s, buffers, completion_condition, throw_error()); +} + +namespace detail +{ + template + class read_handler + { + public: + read_handler(Async_Read_Stream& stream, const Mutable_Buffers& buffers, + Completion_Condition completion_condition, Handler handler) + : stream_(stream), + buffers_(buffers), + total_transferred_(0), + completion_condition_(completion_condition), + handler_(handler) + { + } + + void operator()(const typename Async_Read_Stream::error_type& e, + std::size_t bytes_transferred) + { + total_transferred_ += bytes_transferred; + buffers_.consume(bytes_transferred); + if (completion_condition_(e, total_transferred_) + || buffers_.begin() == buffers_.end()) + { + stream_.demuxer().dispatch( + detail::bind_handler(handler_, e, total_transferred_)); + } + else + { + stream_.async_read_some(buffers_, *this); + } + } + + private: + Async_Read_Stream& stream_; + boost::asio::detail::consuming_buffers buffers_; + std::size_t total_transferred_; + Completion_Condition completion_condition_; + Handler handler_; + }; +} // namespace detail + +template +inline void async_read(Async_Read_Stream& s, const Mutable_Buffers& buffers, + Completion_Condition completion_condition, Handler handler) +{ + s.async_read_some(buffers, + detail::read_handler( + s, buffers, completion_condition, handler)); +} + +template +inline void async_read(Async_Read_Stream& s, const Mutable_Buffers& buffers, + Handler handler) +{ + async_read(s, buffers, transfer_all(), handler); +} + +} // namespace asio +} // namespace boost + +#include + +#endif // BOOST_ASIO_READ_IPP diff --git a/include/boost/asio/impl/write.ipp b/include/boost/asio/impl/write.ipp new file mode 100644 index 00000000..0936745e --- /dev/null +++ b/include/boost/asio/impl/write.ipp @@ -0,0 +1,133 @@ +// +// write.ipp +// ~~~~~~~~~ +// +// Copyright (c) 2003-2005 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_WRITE_IPP +#define BOOST_ASIO_WRITE_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include + +#include +#include +#include +#include + +namespace boost { +namespace asio { + +template +std::size_t write(Sync_Write_Stream& s, const Const_Buffers& buffers, + Completion_Condition completion_condition, Error_Handler error_handler) +{ + boost::asio::detail::consuming_buffers tmp(buffers); + std::size_t total_transferred = 0; + while (tmp.begin() != tmp.end()) + { + typename Sync_Write_Stream::error_type e; + std::size_t bytes_transferred = s.write_some(tmp, assign_error(e)); + tmp.consume(bytes_transferred); + total_transferred += bytes_transferred; + if (completion_condition(e, total_transferred)) + { + if (e) + { + error_handler(e); + } + return total_transferred; + } + } + return total_transferred; +} + +template +inline std::size_t write(Sync_Write_Stream& s, const Const_Buffers& buffers) +{ + return write(s, buffers, transfer_all(), throw_error()); +} + +template +inline std::size_t write(Sync_Write_Stream& s, const Const_Buffers& buffers, + Completion_Condition completion_condition) +{ + return write(s, buffers, completion_condition, throw_error()); +} + +namespace detail +{ + template + class write_handler + { + public: + write_handler(Async_Write_Stream& stream, const Const_Buffers& buffers, + Completion_Condition completion_condition, Handler handler) + : stream_(stream), + buffers_(buffers), + total_transferred_(0), + completion_condition_(completion_condition), + handler_(handler) + { + } + + void operator()(const typename Async_Write_Stream::error_type& e, + std::size_t bytes_transferred) + { + total_transferred_ += bytes_transferred; + buffers_.consume(bytes_transferred); + if (completion_condition_(e, total_transferred_) + || buffers_.begin() == buffers_.end()) + { + stream_.demuxer().dispatch( + detail::bind_handler(handler_, e, total_transferred_)); + } + else + { + stream_.async_write_some(buffers_, *this); + } + } + + private: + Async_Write_Stream& stream_; + boost::asio::detail::consuming_buffers buffers_; + std::size_t total_transferred_; + Completion_Condition completion_condition_; + Handler handler_; + }; +} // namespace detail + +template +inline void async_write(Async_Write_Stream& s, const Const_Buffers& buffers, + Completion_Condition completion_condition, Handler handler) +{ + s.async_write_some(buffers, + detail::write_handler( + s, buffers, completion_condition, handler)); +} + +template +inline void async_write(Async_Write_Stream& s, const Const_Buffers& buffers, + Handler handler) +{ + async_write(s, buffers, transfer_all(), handler); +} + +} // namespace asio +} // namespace boost + +#include + +#endif // BOOST_ASIO_WRITE_IPP diff --git a/include/boost/asio/ipv4/address.hpp b/include/boost/asio/ipv4/address.hpp new file mode 100644 index 00000000..9a608a25 --- /dev/null +++ b/include/boost/asio/ipv4/address.hpp @@ -0,0 +1,231 @@ +// +// address.hpp +// ~~~~~~~~~~~ +// +// Copyright (c) 2003-2005 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_IPV4_ADDRESS_HPP +#define BOOST_ASIO_IPV4_ADDRESS_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include + +#include +#include +#include +#include + +#include +#include +#include + +namespace boost { +namespace asio { +namespace ipv4 { + +/// Implements IP version 4 style addresses. +/** + * The boost::asio::ipv4::address class provides the ability to use and + * manipulate IP version 4 addresses. + * + * @par Thread Safety: + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Unsafe. + */ +class address +{ +public: + /// Default constructor. + address() + { + addr_.s_addr = 0; + } + + /// Construct an address from a unsigned long in host byte order. + address(unsigned long addr) + { + addr_.s_addr = boost::asio::detail::socket_ops::host_to_network_long(addr); + } + + /// Construct an address using an IP address string in dotted decimal form. + address(const char* host) + { + if (boost::asio::detail::socket_ops::inet_pton(AF_INET, host, &addr_) <= 0) + { + boost::asio::error e(boost::asio::detail::socket_ops::get_error()); + boost::throw_exception(e); + } + } + + /// Construct an address using an IP address string in dotted decimal form. + address(const std::string& host) + { + if (boost::asio::detail::socket_ops::inet_pton( + AF_INET, host.c_str(), &addr_) <= 0) + { + boost::asio::error e(boost::asio::detail::socket_ops::get_error()); + boost::throw_exception(e); + } + } + + /// Copy constructor. + address(const address& other) + : addr_(other.addr_) + { + } + + /// Assign from another address. + address& operator=(const address& other) + { + addr_ = other.addr_; + return *this; + } + + /// Assign from an unsigned long. + address& operator=(unsigned long addr) + { + addr_.s_addr = boost::asio::detail::socket_ops::host_to_network_long(addr); + return *this; + } + + /// Assign from an IP address string in dotted decimal form. + address& operator=(const char* addr) + { + address tmp(addr); + addr_ = tmp.addr_; + return *this; + } + + /// Assign from an IP address string in dotted decimal form. + address& operator=(const std::string& addr) + { + address tmp(addr); + addr_ = tmp.addr_; + return *this; + } + + /// Get the address as an unsigned long in host byte order + unsigned long to_ulong() const + { + return boost::asio::detail::socket_ops::network_to_host_long(addr_.s_addr); + } + + /// Get the address as a string in dotted decimal format. + std::string to_string() const + { + char addr_str[boost::asio::detail::max_addr_str_len]; + const char* addr = + boost::asio::detail::socket_ops::inet_ntop(AF_INET, &addr_, addr_str, + boost::asio::detail::max_addr_str_len); + if (addr == 0) + { + boost::asio::error e(boost::asio::detail::socket_ops::get_error()); + boost::throw_exception(e); + } + return addr; + } + + /// Determine whether the address is a class A address. + bool is_class_A() const + { + return IN_CLASSA(to_ulong()); + } + + /// Determine whether the address is a class B address. + bool is_class_B() const + { + return IN_CLASSB(to_ulong()); + } + + /// Determine whether the address is a class C address. + bool is_class_C() const + { + return IN_CLASSC(to_ulong()); + } + + /// Determine whether the address is a class D address. + bool is_class_D() const + { + return IN_CLASSD(to_ulong()); + } + + /// Determine whether the address is a multicast address. + bool is_multicast() const + { + return IN_MULTICAST(to_ulong()); + } + + /// Compare two addresses for equality. + friend bool operator==(const address& a1, const address& a2) + { + return a1.addr_.s_addr == a2.addr_.s_addr; + } + + /// Compare two addresses for inequality. + friend bool operator!=(const address& a1, const address& a2) + { + return a1.addr_.s_addr != a2.addr_.s_addr; + } + + /// Compare addresses for ordering. + friend bool operator<(const address& a1, const address& a2) + { + return a1.to_ulong() < a2.to_ulong(); + } + + /// Obtain an address object that represents any address. + static address any() + { + return address(static_cast(INADDR_ANY)); + } + + /// Obtain an address object that represents the loopback address. + static address loopback() + { + return address(static_cast(INADDR_LOOPBACK)); + } + + /// Obtain an address object that represents the broadcast address. + static address broadcast() + { + return address(static_cast(INADDR_BROADCAST)); + } + +private: + // The underlying IPv4 address. + in_addr addr_; +}; + +/// Output an address as a string. +/** + * Used to output a human-readable string for a specified address. + * + * @param os The output stream to which the string will be written. + * + * @param addr The address to be written. + * + * @return The output stream. + * + * @relates tcp::endpoint + */ +template +Ostream& operator<<(Ostream& os, const address& addr) +{ + os << addr.to_string(); + return os; +} + +} // namespace ipv4 +} // namespace asio +} // namespace boost + +#include + +#endif // BOOST_ASIO_IPV4_ADDRESS_HPP diff --git a/include/boost/asio/ipv4/basic_host_resolver.hpp b/include/boost/asio/ipv4/basic_host_resolver.hpp new file mode 100644 index 00000000..1f9963d2 --- /dev/null +++ b/include/boost/asio/ipv4/basic_host_resolver.hpp @@ -0,0 +1,376 @@ +// +// basic_host_resolver.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2005 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_IPV4_BASIC_HOST_RESOLVER_HPP +#define BOOST_ASIO_IPV4_BASIC_HOST_RESOLVER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +namespace boost { +namespace asio { +namespace ipv4 { + +/// Implements resolution of host names and addresses. +/** + * The boost::asio::ipv4::basic_host_resolver class template provides the + * ability to lookup hosts based on their names or addresses. + * + * Most applications will use the boost::asio::ipv4::host_resolver typedef. + * + * @par Thread Safety: + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Unsafe. + * + * @par Concepts: + * Async_Object, Error_Source. + */ +template +class basic_host_resolver + : private boost::noncopyable +{ +public: + /// The type of the service that will be used to provide host resolution + /// operations. + typedef Service service_type; + + /// The native implementation type of the host resolver. + typedef typename service_type::impl_type impl_type; + + /// The demuxer type for this asynchronous type. + typedef typename service_type::demuxer_type demuxer_type; + + /// The type used for reporting errors. + typedef boost::asio::error error_type; + + /// Construct a basic_host_resolver. + /** + * This constructor creates a basic_host_resolver. + * + * @param d The demuxer object that the host resolver will use to dispatch + * handlers for any asynchronous operations. + */ + explicit basic_host_resolver(demuxer_type& d) + : service_(d.get_service(service_factory())), + impl_(service_.null()) + { + service_.open(impl_); + } + + /// Destructor. + ~basic_host_resolver() + { + service_.close(impl_); + } + + /// Get the demuxer associated with the asynchronous object. + /** + * This function may be used to obtain the demuxer object that the host + * resolver uses to dispatch handlers for asynchronous operations. + * + * @return A reference to the demuxer object that host resolver will use to + * dispatch handlers. Ownership is not transferred to the caller. + */ + demuxer_type& demuxer() + { + return service_.demuxer(); + } + + /// Open the host resolver. + void open() + { + service_.open(impl_); + } + + /// Close the host resolver. + /** + * This function is used to close the host resolver. Any asynchronous + * operations will be cancelled immediately. + */ + void close() + { + service_.close(impl_); + } + + /// Get host information for the local machine. + /** + * This function is used to obtain host information for the local machine. + * + * @param h A host object that receives information about the local machine. + * + * @throws boost::asio::error Thrown on failure. + * + * @par Example: + * @code + * boost::asio::ipv4::host_resolver resolver(demuxer); + * ... + * boost::asio::ipv4::host host; + * resolver.get_local_host(host); + * std::cout << "Name: " << host.name(); + * std::cout << "Address: " << host.addresses(0); + * @endcode + */ + void get_local_host(host& h) + { + service_.get_local_host(impl_, h, throw_error()); + } + + /// Get host information for the local machine. + /** + * This function is used to obtain host information for the local machine. + * + * @param h A host object that receives information associated with the + * specified address. After successful completion of this function, the host + * object is guaranteed to contain at least one address. + * + * @param error_handler The handler to be called when an error occurs. Copies + * will be made of the handler as required. The equivalent function signature + * of the handler must be: + * @code void error_handler( + * const boost::asio::error& error // Result of operation + * ); @endcode + * + * @par Example: + * @code + * boost::asio::ipv4::host_resolver resolver(demuxer); + * ... + * boost::asio::ipv4::host host; + * boost::asio::error error; + * resolver.get_local_host(host, boost::asio::assign_error(error)); + * if (error) + * { + * // An error occurred. + * } + * @endcode + */ + template + void get_local_host(host& h, Error_Handler error_handler) + { + service_.get_local_host(impl_, h, error_handler); + } + + /// Get host information for a specified address. + /** + * This function is used to obtain host information associated with a + * specified address. + * + * @param h A host object that receives information associated with the + * specified address. After successful completion of this function, the host + * object is guaranteed to contain at least one address. + * + * @param addr An address object that identifies a host. + * + * @throws boost::asio::error Thrown on failure. + * + * @par Example: + * @code + * boost::asio::ipv4::host_resolver resolver(demuxer); + * ... + * boost::asio::ipv4::host host; + * boost::asio::ipv4::address address("1.2.3.4"); + * resolver.get_host_by_address(host, address); + * std::cout << "Name: " << host.name(); + * @endcode + */ + void get_host_by_address(host& h, const address& addr) + { + service_.get_host_by_address(impl_, h, addr, throw_error()); + } + + /// Get host information for a specified address. + /** + * This function is used to obtain host information associated with a + * specified address. + * + * @param h A host object that receives information associated with the + * specified address. After successful completion of this function, the host + * object is guaranteed to contain at least one address. + * + * @param addr An address object that identifies a host. + * + * @param error_handler The handler to be called when an error occurs. Copies + * will be made of the handler as required. The equivalent function signature + * of the handler must be: + * @code void error_handler( + * const boost::asio::error& error // Result of operation + * ); @endcode + * + * @par Example: + * @code + * boost::asio::ipv4::host_resolver resolver(demuxer); + * ... + * boost::asio::ipv4::host host; + * boost::asio::ipv4::address address("1.2.3.4"); + * boost::asio::error error; + * resolver.get_host_by_address(host, address, + * boost::asio::assign_error(error)); + * if (error) + * { + * // An error occurred. + * } + * std::cout << "Name: " << host.name(); + * @endcode + */ + template + void get_host_by_address(host& h, const address& addr, + Error_Handler error_handler) + { + service_.get_host_by_address(impl_, h, addr, error_handler); + } + + /// Asynchronously get host information for a specified address. + /** + * This function is used to asynchronously obtain host information associated + * with a specified address. The function call always returns immediately. + * + * @param h A host object that receives information associated with the + * specified address. After successful completion of the asynchronous + * operation, the host object is guaranteed to contain at least one address. + * Ownership of the host object is retained by the caller, which must + * guarantee that it is valid until the handler is called. + * + * @param addr An address object that identifies a host. Copies will be made + * of the address object as required. + * + * @param handler The handler to be called when the resolve operation + * completes. Copies will be made of the handler as required. The equivalent + * function signature of the handler must be: + * @code void handler( + * const boost::asio::error& error // Result of operation + * ); @endcode + */ + template + void async_get_host_by_address(host& h, const address& addr, Handler handler) + { + service_.async_get_host_by_address(impl_, h, addr, handler); + } + + /// Get host information for a named host. + /** + * This function is used to obtain host information associated with a + * specified host name. + * + * @param h A host object that receives information associated with the + * specified host name. + * + * @param name A name that identifies a host. + * + * @throws boost::asio::error Thrown on failure. + * + * @par Example: + * @code + * boost::asio::ipv4::host_resolver resolver(demuxer); + * ... + * boost::asio::ipv4::host host; + * std::string name("myhost"); + * resolver.get_host_by_name(host, name); + * std::cout << "Address: " << host.addresses(0); + * @endcode + */ + void get_host_by_name(host& h, const std::string& name) + { + service_.get_host_by_name(impl_, h, name, throw_error()); + } + + /// Get host information for a named host. + /** + * This function is used to obtain host information associated with a + * specified host name. + * + * @param h A host object that receives information associated with the + * specified host name. + * + * @param name A name that identifies a host. + * + * @param error_handler The handler to be called when an error occurs. Copies + * will be made of the handler as required. The equivalent function signature + * of the handler must be: + * @code void error_handler( + * const boost::asio::error& error // Result of operation + * ); @endcode + * + * @par Example: + * @code + * boost::asio::ipv4::host_resolver resolver(demuxer); + * ... + * boost::asio::ipv4::host host; + * std::string name("myhost"); + * boost::asio::error error; + * resolver.get_host_by_name(host, name, boost::asio::assign_error(error)); + * if (error) + * { + * // An error occurred. + * } + * std::cout << "Address: " << host.addresses(0); + * @endcode + */ + template + void get_host_by_name(host& h, const std::string& name, + Error_Handler error_handler) + { + service_.get_host_by_name(impl_, h, name, error_handler); + } + + /// Asynchronously get host information for a named host. + /** + * This function is used to asynchronously obtain host information associated + * with a specified host name. The function call always returns immediately. + * + * @param h A host object that receives information associated with the + * specified address. After successful completion of the asynchronous + * operation, the host object is guaranteed to contain at least one address. + * Ownership of the host object is retained by the caller, which must + * guarantee that it is valid until the handler is called. + * + * @param name A name that identifies a host. Copies will be made of the name + * as required. + * + * @param handler The handler to be called when the resolve operation + * completes. Copies will be made of the handler as required. The equivalent + * function signature of the handler must be: + * @code void handler( + * const boost::asio::error& error // Result of operation + * ); @endcode + */ + template + void async_get_host_by_name(host& h, const std::string& name, Handler handler) + { + service_.async_get_host_by_name(impl_, h, name, handler); + } + +private: + /// The backend service implementation. + service_type& service_; + + /// The underlying native implementation. + impl_type impl_; +}; + +} // namespace ipv4 +} // namespace asio +} // namespace boost + +#include + +#endif // BOOST_ASIO_IPV4_BASIC_HOST_RESOLVER_HPP diff --git a/include/boost/asio/ipv4/detail/host_resolver_service.hpp b/include/boost/asio/ipv4/detail/host_resolver_service.hpp new file mode 100644 index 00000000..41aefddb --- /dev/null +++ b/include/boost/asio/ipv4/detail/host_resolver_service.hpp @@ -0,0 +1,375 @@ +// +// host_resolver_service.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2005 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_IPV4_DETAIL_HOST_RESOLVER_SERVICE_HPP +#define BOOST_ASIO_IPV4_DETAIL_HOST_RESOLVER_SERVICE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace boost { +namespace asio { +namespace ipv4 { +namespace detail { + +template +class host_resolver_service + : private boost::noncopyable +{ +private: + // Helper class to perform exception-safe cleanup of hostent objects. + class auto_hostent + : private boost::noncopyable + { + public: + explicit auto_hostent(hostent* h) + : h_(h) + { + } + + ~auto_hostent() + { + if (h_) + boost::asio::detail::socket_ops::freehostent(h_); + } + + operator hostent*() + { + return h_; + } + + private: + hostent* h_; + }; + +public: + // Implementation structure for a host resolver. + struct resolver_impl + : private boost::noncopyable + { + }; + + // The native type of the host resolver. + typedef boost::shared_ptr impl_type; + + // Return a null host resolver implementation. + static impl_type null() + { + return impl_type(); + } + + // Constructor. + host_resolver_service(Demuxer& d) + : mutex_(), + demuxer_(d), + work_demuxer_(), + work_(new typename Demuxer::work(work_demuxer_)), + work_thread_(0) + { + } + + // Destructor. + ~host_resolver_service() + { + delete work_; + if (work_thread_) + { + work_thread_->join(); + delete work_thread_; + } + } + + // The demuxer type for this service. + typedef Demuxer demuxer_type; + + // Get the demuxer associated with the service. + demuxer_type& demuxer() + { + return demuxer_; + } + + // Open a new host resolver implementation. + void open(impl_type& impl) + { + impl = impl_type(new resolver_impl); + } + + // Close a host resolver implementation. + void close(impl_type& impl) + { + impl = null(); + } + + // Get host information for the local machine. + template + void get_local_host(impl_type& impl, host& h, Error_Handler error_handler) + { + char name[1024]; + if (boost::asio::detail::socket_ops::gethostname(name, sizeof(name)) != 0) + { + error_handler(boost::asio::error( + boost::asio::detail::socket_ops::get_error())); + } + else + { + get_host_by_name(impl, h, name, error_handler); + } + } + + // Get host information for a specified address. + template + void get_host_by_address(impl_type& impl, host& h, const address& addr, + Error_Handler error_handler) + { + hostent ent; + char buf[8192] = ""; // Size recommended by Stevens, UNPv1. + int error = 0; + in_addr a; + a.s_addr = boost::asio::detail::socket_ops::host_to_network_long( + addr.to_ulong()); + auto_hostent result(boost::asio::detail::socket_ops::gethostbyaddr( + reinterpret_cast(&a), sizeof(in_addr), AF_INET, &ent, + buf, sizeof(buf), &error)); + if (result == 0) + error_handler(boost::asio::error(error)); + else if (ent.h_length != sizeof(in_addr)) + error_handler(boost::asio::error(boost::asio::error::host_not_found)); + else + populate_host_object(h, ent); + } + + template + class get_host_by_address_handler + { + public: + get_host_by_address_handler(impl_type impl, host& h, const address& addr, + Demuxer& demuxer, Handler handler) + : impl_(impl), + host_(h), + address_(addr), + demuxer_(demuxer), + work_(demuxer), + handler_(handler) + { + } + + void operator()() + { + // Check if the operation has been cancelled. + if (impl_.expired()) + { + demuxer_.post(boost::asio::detail::bind_handler(handler_, + boost::asio::error(boost::asio::error::operation_aborted))); + return; + } + + // Perform the blocking host resolution operation. + hostent ent; + char buf[8192] = ""; // Size recommended by Stevens, UNPv1. + int error = 0; + in_addr a; + a.s_addr = boost::asio::detail::socket_ops::host_to_network_long( + address_.to_ulong()); + auto_hostent result(boost::asio::detail::socket_ops::gethostbyaddr( + reinterpret_cast(&a), sizeof(in_addr), AF_INET, &ent, + buf, sizeof(buf), &error)); + boost::asio::error e(boost::asio::error::success); + if (result == 0) + e = boost::asio::error(error); + else if (ent.h_length != sizeof(in_addr)) + e = boost::asio::error(boost::asio::error::host_not_found); + else + populate_host_object(host_, ent); + demuxer_.post(boost::asio::detail::bind_handler(handler_, e)); + } + + private: + boost::weak_ptr impl_; + host& host_; + address address_; + Demuxer& demuxer_; + typename Demuxer::work work_; + Handler handler_; + }; + + // Asynchronously get host information for a specified address. + template + void async_get_host_by_address(impl_type& impl, host& h, const address& addr, + Handler handler) + { + start_work_thread(); + work_demuxer_.post(get_host_by_address_handler( + impl, h, addr, demuxer_, handler)); + } + + // Get host information for a named host. + template + void get_host_by_name(impl_type& impl, host& h, const std::string& name, + Error_Handler error_handler) + { + hostent ent; + char buf[8192] = ""; // Size recommended by Stevens, UNPv1. + int error = 0; + auto_hostent result(boost::asio::detail::socket_ops::gethostbyname( + name.c_str(), &ent, buf, sizeof(buf), &error)); + if (result == 0) + error_handler(boost::asio::error(error)); + else if (ent.h_addrtype != AF_INET || ent.h_length != sizeof(in_addr)) + error_handler(boost::asio::error(boost::asio::error::host_not_found)); + else + populate_host_object(h, ent); + } + + template + class get_host_by_name_handler + { + public: + get_host_by_name_handler(impl_type impl, host& h, const std::string& name, + Demuxer& demuxer, Handler handler) + : impl_(impl), + host_(h), + name_(name), + demuxer_(demuxer), + work_(demuxer), + handler_(handler) + { + } + + void operator()() + { + // Check if the operation has been cancelled. + if (impl_.expired()) + { + demuxer_.post(boost::asio::detail::bind_handler(handler_, + boost::asio::error(boost::asio::error::operation_aborted))); + return; + } + + // Perform the blocking host resolution operation. + hostent ent; + char buf[8192] = ""; // Size recommended by Stevens, UNPv1. + int error = 0; + auto_hostent result(boost::asio::detail::socket_ops::gethostbyname( + name_.c_str(), &ent, buf, sizeof(buf), &error)); + boost::asio::error e(boost::asio::error::success); + if (result == 0) + e = boost::asio::error(error); + else if (ent.h_addrtype != AF_INET || ent.h_length != sizeof(in_addr)) + e = boost::asio::error(boost::asio::error::host_not_found); + else + populate_host_object(host_, ent); + demuxer_.post(boost::asio::detail::bind_handler(handler_, e)); + } + + private: + boost::weak_ptr impl_; + host& host_; + std::string name_; + Demuxer& demuxer_; + typename Demuxer::work work_; + Handler handler_; + }; + + // Asynchronously get host information for a named host. + template + void async_get_host_by_name(impl_type& impl, host& h, const std::string& name, + Handler handler) + { + start_work_thread(); + work_demuxer_.post(get_host_by_name_handler( + impl, h, name, demuxer_, handler)); + } + + // Populate a host object from a hostent structure. + static void populate_host_object(host& h, hostent& ent) + { + std::vector aliases; + for (char** alias = ent.h_aliases; *alias; ++alias) + aliases.push_back(*alias); + + std::vector
addresses; + for (char** addr = ent.h_addr_list; *addr; ++addr) + { + using namespace std; // For memcpy. + in_addr a; + memcpy(&a, *addr, sizeof(in_addr)); + addresses.push_back(address( + boost::asio::detail::socket_ops::network_to_host_long(a.s_addr))); + } + + host tmp(ent.h_name, addresses.front(), aliases.begin(), aliases.end(), + addresses.begin() + 1, addresses.end()); + h.swap(tmp); + } + +private: + // Helper class to run the work demuxer in a thread. + class work_demuxer_runner + { + public: + work_demuxer_runner(Demuxer& demuxer) : demuxer_(demuxer) {} + void operator()() { demuxer_.run(); } + private: + Demuxer& demuxer_; + }; + + // Start the work thread if it's not already running. + void start_work_thread() + { + boost::asio::detail::mutex::scoped_lock lock(mutex_); + if (work_thread_ == 0) + { + work_thread_ = new boost::asio::detail::thread( + work_demuxer_runner(work_demuxer_)); + } + } + + // Mutex to protect access to internal data. + boost::asio::detail::mutex mutex_; + + // The demuxer used for dispatching handlers. + Demuxer& demuxer_; + + // Private demuxer used for performing asynchronous host resolution. + Demuxer work_demuxer_; + + // Work for the private demuxer to perform. + typename Demuxer::work* work_; + + // Thread used for running the work demuxer's run loop. + boost::asio::detail::thread* work_thread_; +}; + +} // namespace detail +} // namespace ipv4 +} // namespace asio +} // namespace boost + +#include + +#endif // BOOST_ASIO_IPV4_DETAIL_HOST_RESOLVER_SERVICE_HPP diff --git a/include/boost/asio/ipv4/detail/socket_option.hpp b/include/boost/asio/ipv4/detail/socket_option.hpp new file mode 100644 index 00000000..6fe47be0 --- /dev/null +++ b/include/boost/asio/ipv4/detail/socket_option.hpp @@ -0,0 +1,183 @@ +// +// socket_option.hpp +// ~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2005 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_IPV4_DETAIL_SOCKET_OPTION_HPP +#define BOOST_ASIO_IPV4_DETAIL_SOCKET_OPTION_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include + +#include +#include +#include +#include + +#include +#include +#include + +namespace boost { +namespace asio { +namespace ipv4 { +namespace detail { +namespace socket_option { + +// Helper template for implementing address-based options. +template +class address +{ +public: + // Default constructor. + address() + { + value_.s_addr = boost::asio::detail::socket_ops::host_to_network_long( + boost::asio::ipv4::address::any().to_ulong()); + } + + // Construct with address. + address(const boost::asio::ipv4::address& value) + { + value_.s_addr = + boost::asio::detail::socket_ops::host_to_network_long(value.to_ulong()); + } + + // Get the level of the socket option. + int level() const + { + return Level; + } + + // Get the name of the socket option. + int name() const + { + return Name; + } + + // Set the value of the socket option. + void set(const boost::asio::ipv4::address& value) + { + value_.s_addr = + boost::asio::detail::socket_ops::host_to_network_long(value.to_ulong()); + } + + // Get the current value of the socket option. + boost::asio::ipv4::address get() const + { + return boost::asio::ipv4::address( + boost::asio::detail::socket_ops::network_to_host_long(value_.s_addr)); + } + + // Get the address of the option data. + in_addr* data() + { + return &value_; + } + + // Get the address of the option data. + const in_addr* data() const + { + return &value_; + } + + // Get the size of the option data. + std::size_t size() const + { + return sizeof(value_); + } + +private: + in_addr value_; +}; + +// Helper template for implementing ip_mreq-based options. +template +class multicast_request +{ +public: + // Default constructor. + multicast_request() + { + value_.imr_multiaddr.s_addr = + boost::asio::detail::socket_ops::host_to_network_long( + boost::asio::ipv4::address::any().to_ulong()); + value_.imr_interface.s_addr = + boost::asio::detail::socket_ops::host_to_network_long( + boost::asio::ipv4::address::any().to_ulong()); + } + + // Construct with multicast address only. + multicast_request(const boost::asio::ipv4::address& multicast_address) + { + value_.imr_multiaddr.s_addr = + boost::asio::detail::socket_ops::host_to_network_long( + multicast_address.to_ulong()); + value_.imr_interface.s_addr = + boost::asio::detail::socket_ops::host_to_network_long( + boost::asio::ipv4::address::any().to_ulong()); + } + + // Construct with multicast address and address of local interface to use. + multicast_request(const boost::asio::ipv4::address& multicast_address, + const boost::asio::ipv4::address& local_address) + { + value_.imr_multiaddr.s_addr = + boost::asio::detail::socket_ops::host_to_network_long( + multicast_address.to_ulong()); + value_.imr_interface.s_addr = + boost::asio::detail::socket_ops::host_to_network_long( + local_address.to_ulong()); + } + + // Get the level of the socket option. + int level() const + { + return Level; + } + + // Get the name of the socket option. + int name() const + { + return Name; + } + + // Get the address of the option data. + ip_mreq* data() + { + return &value_; + } + + // Get the address of the option data. + const ip_mreq* data() const + { + return &value_; + } + + // Get the size of the option data. + std::size_t size() const + { + return sizeof(value_); + } + +private: + ip_mreq value_; +}; + +} // namespace socket_option +} // namespace detail +} // namespace ipv4 +} // namespace asio +} // namespace boost + +#include + +#endif // BOOST_ASIO_IPV4_DETAIL_SOCKET_OPTION_HPP diff --git a/include/boost/asio/ipv4/host.hpp b/include/boost/asio/ipv4/host.hpp new file mode 100644 index 00000000..62d83c38 --- /dev/null +++ b/include/boost/asio/ipv4/host.hpp @@ -0,0 +1,130 @@ +// +// host.hpp +// ~~~~~~~~ +// +// Copyright (c) 2003-2005 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_IPV4_HOST_HPP +#define BOOST_ASIO_IPV4_HOST_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include + +#include +#include +#include +#include +#include +#include + +#include + +namespace boost { +namespace asio { +namespace ipv4 { + +/// Encapsulates information about an IP version 4 host. +/** + * The boost::asio::ipv4::host structure contains properties to describe a host. + * + * @par Thread Safety: + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Unsafe. + * + * @par Concepts: + * CopyConstructible, Assignable. + */ +class host +{ +public: + /// Default constructor. + host() + { + } + + /// Construct from component properties. + host(const std::string& name, const boost::asio::ipv4::address& addr) + : name_(name) + { + addresses_.push_back(boost::asio::ipv4::address::any()); + } + + /// Construct from component properties. + template + host(const std::string& name, const boost::asio::ipv4::address& addr, + Name_Iterator alternate_names_begin, + Name_Iterator alternate_names_end, + Address_Iterator other_addresses_begin, + Address_Iterator other_addresses_end) + : name_(name) + { + addresses_.push_back(addr); + alternate_names_.insert(alternate_names_.end(), + alternate_names_begin, alternate_names_end); + addresses_.insert(addresses_.end(), + other_addresses_begin, other_addresses_end); + } + + /// Get the name of the host. + std::string name() const + { + return name_; + } + + /// Get the number of alternate names for the host. + std::size_t alternate_name_count() const + { + return alternate_names_.size(); + } + + /// Get the alternate name at the specified index. + std::string alternate_name(std::size_t index) const + { + return alternate_names_[index]; + } + + /// Get the number of addresses for the host. + std::size_t address_count() const + { + return addresses_.size(); + } + + /// Get the address at the specified index. + boost::asio::ipv4::address address(std::size_t index) const + { + return addresses_[index]; + } + + /// Swap the host object's values with another host. + void swap(host& other) + { + name_.swap(other.name_); + alternate_names_.swap(other.alternate_names_); + addresses_.swap(other.addresses_); + } + +private: + // The name of the host. + std::string name_; + + // Alternate names for the host. + std::vector alternate_names_; + + // All IP addresses of the host. + std::vector addresses_; +}; + +} // namespace ipv4 +} // namespace asio +} // namespace boost + +#include + +#endif // BOOST_ASIO_IPV4_HOST_HPP diff --git a/include/boost/asio/ipv4/host_resolver.hpp b/include/boost/asio/ipv4/host_resolver.hpp new file mode 100644 index 00000000..7a68c5b3 --- /dev/null +++ b/include/boost/asio/ipv4/host_resolver.hpp @@ -0,0 +1,37 @@ +// +// host_resolver.hpp +// ~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2005 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_IPV4_HOST_RESOLVER_HPP +#define BOOST_ASIO_IPV4_HOST_RESOLVER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include + +#include +#include +#include + +namespace boost { +namespace asio { +namespace ipv4 { + +/// Typedef for the typical usage of host_resolver. +typedef basic_host_resolver > host_resolver; + +} // namespace ipv4 +} // namespace asio +} // namespace boost + +#include + +#endif // BOOST_ASIO_IPV4_HOST_RESOLVER_HPP diff --git a/include/boost/asio/ipv4/host_resolver_service.hpp b/include/boost/asio/ipv4/host_resolver_service.hpp new file mode 100644 index 00000000..91521e36 --- /dev/null +++ b/include/boost/asio/ipv4/host_resolver_service.hpp @@ -0,0 +1,139 @@ +// +// host_resolver_service.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2005 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_IPV4_HOST_RESOLVER_SERVICE_HPP +#define BOOST_ASIO_IPV4_HOST_RESOLVER_SERVICE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace boost { +namespace asio { +namespace ipv4 { + +/// Default service implementation for a host resolver. +template > +class host_resolver_service + : private boost::noncopyable +{ +public: + /// The demuxer type for this service. + typedef basic_demuxer > demuxer_type; + +private: + // The type of the platform-specific implementation. + typedef detail::host_resolver_service service_impl_type; + +public: + /// The type of the host resolver. +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined impl_type; +#else + typedef typename service_impl_type::impl_type impl_type; +#endif + + /// Constructor. + host_resolver_service(demuxer_type& demuxer) + : service_impl_(demuxer.get_service(service_factory())) + { + } + + /// Get the demuxer associated with the service. + demuxer_type& demuxer() + { + return service_impl_.demuxer(); + } + + /// Return a null host resolver implementation. + impl_type null() const + { + return service_impl_.null(); + } + + /// Open a new host resolver implementation. + void open(impl_type& impl) + { + service_impl_.open(impl); + } + + /// Close a host resolver implementation. + void close(impl_type& impl) + { + service_impl_.close(impl); + } + + /// Get host information for the local machine. + template + void get_local_host(impl_type& impl, host& h, Error_Handler error_handler) + { + service_impl_.get_local_host(impl, h, error_handler); + } + + /// Get host information for a specified address. + template + void get_host_by_address(impl_type& impl, host& h, const address& addr, + Error_Handler error_handler) + { + service_impl_.get_host_by_address(impl, h, addr, error_handler); + } + + // Asynchronously get host information for a specified address. + template + void async_get_host_by_address(impl_type& impl, host& h, const address& addr, + Handler handler) + { + service_impl_.async_get_host_by_address(impl, h, addr, handler); + } + + /// Get host information for a named host. + template + void get_host_by_name(impl_type& impl, host& h, const std::string& name, + Error_Handler error_handler) + { + service_impl_.get_host_by_name(impl, h, name, error_handler); + } + + // Asynchronously get host information for a named host. + template + void async_get_host_by_name(impl_type& impl, host& h, const std::string& name, + Handler handler) + { + service_impl_.async_get_host_by_name(impl, h, name, handler); + } + +private: + // The service that provides the platform-specific implementation. + service_impl_type& service_impl_; +}; + +} // namespace ipv4 +} // namespace asio +} // namespace boost + +#include + +#endif // BOOST_ASIO_IPV4_HOST_RESOLVER_SERVICE_HPP diff --git a/include/boost/asio/ipv4/multicast.hpp b/include/boost/asio/ipv4/multicast.hpp new file mode 100644 index 00000000..72d40edb --- /dev/null +++ b/include/boost/asio/ipv4/multicast.hpp @@ -0,0 +1,215 @@ +// +// multicast.hpp +// ~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2005 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_IPV4_MULTICAST_HPP +#define BOOST_ASIO_IPV4_MULTICAST_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include + +#include +#include +#include +#include + +#include +#include + +namespace boost { +namespace asio { +namespace ipv4 { +namespace multicast { + +/// Socket option to join a multicast group on a specified interface. +/** + * Implements the IPPROTO_IP/IP_ADD_MEMBERSHIP socket option. + * + * @par Examples: + * Setting the option to join a multicast group: + * @code + * boost::asio::datagram_socket socket(demuxer); + * ... + * boost::asio::ipv4::address multicast_address("225.0.0.1"); + * boost::asio::ipv4::multicast::add_membership option(multicast_address); + * socket.set_option(option); + * @endcode + * + * @par + * Setting the option to join a multicast group on the specified local + * interface. + * @code + * boost::asio::datagram_socket socket(demuxer); + * boost::asio::ipv4::address multicast_address("225.0.0.1"); + * boost::asio::ipv4::address local_interface("1.2.3.4"); + * boost::asio::ipv4::multicast::add_membership option( + * multicast_address, local_interface); + * socket.set_option(option); + * ... + * @endcode + * + * @par Concepts: + * Socket_Option, IPv4_MReq_Socket_Option. + */ +#if defined(GENERATING_DOCUMENTATION) +typedef implementation_defined add_membership; +#else +typedef boost::asio::ipv4::detail::socket_option::multicast_request< + IPPROTO_IP, IP_ADD_MEMBERSHIP> add_membership; +#endif + +/// Socket option to leave a multicast group on a specified interface. +/** + * Implements the IPPROTO_IP/IP_DROP_MEMBERSHIP socket option. + * + * @par Examples: + * Setting the option to leave a multicast group: + * @code + * boost::asio::datagram_socket socket(demuxer); + * ... + * boost::asio::ipv4::address multicast_address("225.0.0.1"); + * boost::asio::ipv4::multicast::drop_membership option(multicast_address); + * socket.set_option(option); + * @endcode + * + * @par + * Setting the option to leave a multicast group on the specified local + * interface. + * @code + * boost::asio::datagram_socket socket(demuxer); + * boost::asio::ipv4::address multicast_address("225.0.0.1"); + * boost::asio::ipv4::address local_interface("1.2.3.4"); + * boost::asio::ipv4::multicast::drop_membership option( + * multicast_address, local_interface); + * socket.set_option(option); + * ... + * @endcode + * + * @par Concepts: + * Socket_Option, IPv4_MReq_Socket_Option. + */ +#if defined(GENERATING_DOCUMENTATION) +typedef implementation_defined drop_membership; +#else +typedef boost::asio::ipv4::detail::socket_option::multicast_request< + IPPROTO_IP, IP_DROP_MEMBERSHIP> drop_membership; +#endif + +/// Socket option for local interface to use for outgoing multicast packets. +/** + * Implements the IPPROTO_IP/IP_MULTICAST_IF socket option. + * + * @par Examples: + * Setting the option: + * @code + * boost::asio::datagram_socket socket(demuxer); + * ... + * boost::asio::ipv4::address local_interface("1.2.3.4"); + * boost::asio::ipv4::multicast::outbound_interface option(local_interface); + * socket.set_option(option); + * @endcode + * + * @par + * Getting the current option value: + * @code + * boost::asio::datagram_socket socket(demuxer); + * ... + * boost::asio::ipv4::multicast::outbound_interface option; + * socket.get_option(option); + * boost::asio::ipv4::address local_interface = option.get(); + * @endcode + * + * @par Concepts: + * Socket_Option, IPv4_Address_Socket_Option. + */ +#if defined(GENERATING_DOCUMENTATION) +typedef implementation_defined outbound_interface; +#else +typedef boost::asio::ipv4::detail::socket_option::address< + IPPROTO_IP, IP_MULTICAST_IF> outbound_interface; +#endif + +/// Socket option for time-to-live associated with outgoing multicast packets. +/** + * Implements the IPPROTO_IP/IP_MULTICAST_TTL socket option. + * + * @par Examples: + * Setting the option: + * @code + * boost::asio::datagram_socket socket(demuxer); + * ... + * boost::asio::ipv4::multicast::time_to_live option(4); + * socket.set_option(option); + * @endcode + * + * @par + * Getting the current option value: + * @code + * boost::asio::datagram_socket socket(demuxer); + * ... + * boost::asio::ipv4::multicast::time_to_live option; + * socket.get_option(option); + * int ttl = option.get(); + * @endcode + * + * @par Concepts: + * Socket_Option, Integer_Socket_Option. + */ +#if defined(GENERATING_DOCUMENTATION) +typedef implementation_defined time_to_live; +#else +typedef boost::asio::detail::socket_option::integer< + IPPROTO_IP, IP_MULTICAST_TTL> time_to_live; +#endif + +/// Socket option determining whether outgoing multicast packets will be +/// received on the same socket if it is a member of the multicast group. +/** + * Implements the IPPROTO_IP/IP_MULTICAST_LOOP socket option. + * + * @par Examples: + * Setting the option: + * @code + * boost::asio::datagram_socket socket(demuxer); + * ... + * boost::asio::ipv4::multicast::enable_loopback option(true); + * socket.set_option(option); + * @endcode + * + * @par + * Getting the current option value: + * @code + * boost::asio::datagram_socket socket(demuxer); + * ... + * boost::asio::ipv4::multicast::enable_loopback option; + * socket.get_option(option); + * bool is_set = option.get(); + * @endcode + * + * @par Concepts: + * Socket_Option, Boolean_Socket_Option. + */ +#if defined(GENERATING_DOCUMENTATION) +typedef implementation_defined enable_loopback; +#else +typedef boost::asio::detail::socket_option::boolean< + IPPROTO_IP, IP_MULTICAST_LOOP> enable_loopback; +#endif + +} // namespace multicast +} // namespace ipv4 +} // namespace asio +} // namespace boost + +#include + +#endif // BOOST_ASIO_IPV4_MULTICAST_HPP diff --git a/include/boost/asio/ipv4/tcp.hpp b/include/boost/asio/ipv4/tcp.hpp new file mode 100644 index 00000000..0f723e78 --- /dev/null +++ b/include/boost/asio/ipv4/tcp.hpp @@ -0,0 +1,308 @@ +// +// tcp.hpp +// ~~~~~~~ +// +// Copyright (c) 2003-2005 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_IPV4_TCP_HPP +#define BOOST_ASIO_IPV4_TCP_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include + +#include +#include +#include +#if BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564)) +# include +#endif // BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564)) +#include + +#include +#include +#include +#include +#include + +namespace boost { +namespace asio { +namespace ipv4 { + +/// Encapsulates the flags needed for TCP. +/** + * The boost::asio::ipv4::tcp class contains flags necessary for TCP sockets. + * + * @par Thread Safety: + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Safe. + * + * @par Concepts: + * Protocol. + */ +class tcp +{ +public: + class endpoint; + + /// Obtain an identifier for the type of the protocol. + int type() const + { + return SOCK_STREAM; + } + + /// Obtain an identifier for the protocol. + int protocol() const + { + return IPPROTO_TCP; + } + + /// Obtain an identifier for the protocol family. + int family() const + { + return PF_INET; + } + + /// Socket option for disabling the Nagle algorithm. + /** + * Implements the IPPROTO_TCP/TCP_NODELAY socket option. + * + * @par Examples: + * Setting the option: + * @code + * boost::asio::stream_socket socket(demuxer); + * ... + * boost::asio::ipv4::tcp::no_delay option(true); + * socket.set_option(option); + * @endcode + * + * @par + * Getting the current option value: + * @code + * boost::asio::stream_socket socket(demuxer); + * ... + * boost::asio::ipv4::tcp::no_delay option; + * socket.get_option(option); + * bool is_set = option.get(); + * @endcode + * + * @par Concepts: + * Socket_Option, Boolean_Socket_Option. + */ +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined no_delay; +#else + typedef boost::asio::detail::socket_option::boolean< + IPPROTO_TCP, TCP_NODELAY> no_delay; +#endif +}; + +/// Describes an endpoint for a TCP socket. +/** + * The boost::asio::ipv4::tcp::endpoint class describes an endpoint that may be + * associated with a particular socket. + * + * @par Thread Safety: + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Unsafe. + * + * @par Concepts: + * Endpoint. + */ +class tcp::endpoint +{ +public: + /// The protocol type associated with the endpoint. + typedef tcp protocol_type; + + /// The type of the endpoint structure. This type is dependent on the + /// underlying implementation of the socket layer. +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined data_type; +#else + typedef boost::asio::detail::socket_addr_type data_type; +#endif + + /// The type for the size of the endpoint structure. This type is dependent on + /// the underlying implementation of the socket layer. +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined size_type; +#else + typedef boost::asio::detail::socket_addr_len_type size_type; +#endif + + /// Default constructor. + endpoint() + { + addr_.sin_family = AF_INET; + addr_.sin_port = 0; + addr_.sin_addr.s_addr = INADDR_ANY; + } + + /// Construct an endpoint using a port number, specified in the host's byte + /// order. The IP address will be the any address (i.e. INADDR_ANY). This + /// constructor would typically be used for accepting new connections. + endpoint(unsigned short port_num) + { + addr_.sin_family = AF_INET; + addr_.sin_port = + boost::asio::detail::socket_ops::host_to_network_short(port_num); + addr_.sin_addr.s_addr = INADDR_ANY; + } + + /// Construct an endpoint using a port number and an IP address. This + /// constructor may be used for accepting connections on a specific interface + /// or for making a connection to a remote endpoint. + endpoint(unsigned short port_num, const boost::asio::ipv4::address& addr) + { + addr_.sin_family = AF_INET; + addr_.sin_port = + boost::asio::detail::socket_ops::host_to_network_short(port_num); + addr_.sin_addr.s_addr = + boost::asio::detail::socket_ops::host_to_network_long(addr.to_ulong()); + } + + /// Copy constructor. + endpoint(const endpoint& other) + : addr_(other.addr_) + { + } + + /// Assign from another endpoint. + endpoint& operator=(const endpoint& other) + { + addr_ = other.addr_; + return *this; + } + + /// The protocol associated with the endpoint. + protocol_type protocol() const + { + return protocol_type(); + } + + /// Get the underlying endpoint in the native type. + data_type* data() + { + return reinterpret_cast(&addr_); + } + + /// Get the underlying endpoint in the native type. + const data_type* data() const + { + return reinterpret_cast(&addr_); + } + + /// Get the underlying size of the endpoint in the native type. + size_type size() const + { + return sizeof(addr_); + } + + /// Set the underlying size of the endpoint in the native type. + void size(size_type size) + { + if (size != sizeof(addr_)) + { + boost::asio::error e(boost::asio::error::invalid_argument); + boost::throw_exception(e); + } + } + + /// Get the port associated with the endpoint. The port number is always in + /// the host's byte order. + unsigned short port() const + { + return boost::asio::detail::socket_ops::network_to_host_short( + addr_.sin_port); + } + + /// Set the port associated with the endpoint. The port number is always in + /// the host's byte order. + void port(unsigned short port_num) + { + addr_.sin_port = + boost::asio::detail::socket_ops::host_to_network_short(port_num); + } + + /// Get the IP address associated with the endpoint. + boost::asio::ipv4::address address() const + { + return boost::asio::detail::socket_ops::network_to_host_long( + addr_.sin_addr.s_addr); + } + + /// Set the IP address associated with the endpoint. + void address(const boost::asio::ipv4::address& addr) + { + addr_.sin_addr.s_addr = + boost::asio::detail::socket_ops::host_to_network_long(addr.to_ulong()); + } + + /// Compare two endpoints for equality. + friend bool operator==(const endpoint& e1, const endpoint& e2) + { + return e1.address() == e2.address() && e1.port() == e2.port(); + } + + /// Compare two endpoints for inequality. + friend bool operator!=(const endpoint& e1, const endpoint& e2) + { + return e1.address() != e2.address() || e1.port() != e2.port(); + } + + /// Compare endpoints for ordering. + friend bool operator<(const endpoint& e1, const endpoint& e2) + { + if (e1.address() < e2.address()) + return true; + if (e1.address() != e2.address()) + return false; + return e1.port() < e2.port(); + } + +private: + // The underlying IPv4 socket address. + boost::asio::detail::inet_addr_v4_type addr_; +}; + +/// Output an endpoint as a string. +/** + * Used to output a human-readable string for a specified endpoint. + * + * @param os The output stream to which the string will be written. + * + * @param endpoint The endpoint to be written. + * + * @return The output stream. + * + * @relates tcp::endpoint + */ +#if BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564)) +std::ostream& operator<<(std::ostream& os, const tcp::endpoint& endpoint) +{ + os << endpoint.address().to_string() << ':' << endpoint.port(); + return os; +} +#else // BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564)) +template +Ostream& operator<<(Ostream& os, const tcp::endpoint& endpoint) +{ + os << endpoint.address().to_string() << ':' << endpoint.port(); + return os; +} +#endif // BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564)) + +} // namespace ipv4 +} // namespace asio +} // namespace boost + +#include + +#endif // BOOST_ASIO_IPV4_TCP_HPP diff --git a/include/boost/asio/ipv4/udp.hpp b/include/boost/asio/ipv4/udp.hpp new file mode 100644 index 00000000..12107995 --- /dev/null +++ b/include/boost/asio/ipv4/udp.hpp @@ -0,0 +1,274 @@ +// +// udp.hpp +// ~~~~~~~ +// +// Copyright (c) 2003-2005 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_IPV4_UDP_HPP +#define BOOST_ASIO_IPV4_UDP_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include + +#include +#include +#include +#if BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564)) +# include +#endif // BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564)) +#include + +#include +#include +#include +#include + +namespace boost { +namespace asio { +namespace ipv4 { + +/// Encapsulates the flags needed for UDP. +/** + * The boost::asio::ipv4::udp class contains flags necessary for UDP sockets. + * + * @par Thread Safety: + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Safe. + * + * @par Concepts: + * Protocol. + */ +class udp +{ +public: + class endpoint; + + /// Obtain an identifier for the type of the protocol. + int type() const + { + return SOCK_DGRAM; + } + + /// Obtain an identifier for the protocol. + int protocol() const + { + return IPPROTO_UDP; + } + + /// Obtain an identifier for the protocol family. + int family() const + { + return PF_INET; + } +}; + +/// Describes an endpoint for a UDP socket. +/** + * The boost::asio::ipv4::udp::endpoint class describes an endpoint that may be + * associated with a particular socket. + * + * @par Thread Safety: + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Unsafe. + * + * @par Concepts: + * Endpoint. + */ +class udp::endpoint +{ +public: + /// The protocol type associated with the endpoint. + typedef udp protocol_type; + + /// The type of the endpoint structure. This type is dependent on the + /// underlying implementation of the socket layer. +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined data_type; +#else + typedef boost::asio::detail::socket_addr_type data_type; +#endif + + /// The type for the size of the endpoint structure. This type is dependent on + /// the underlying implementation of the socket layer. +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined size_type; +#else + typedef boost::asio::detail::socket_addr_len_type size_type; +#endif + + /// Default constructor. + endpoint() + { + addr_.sin_family = AF_INET; + addr_.sin_port = 0; + addr_.sin_addr.s_addr = INADDR_ANY; + } + + /// Construct an endpoint using a port number, specified in the host's byte + /// order. The IP address will be the any address (i.e. INADDR_ANY). This + /// constructor would typically be used for accepting new connections. + endpoint(unsigned short port_num) + { + addr_.sin_family = AF_INET; + addr_.sin_port = + boost::asio::detail::socket_ops::host_to_network_short(port_num); + addr_.sin_addr.s_addr = INADDR_ANY; + } + + /// Construct an endpoint using a port number and an IP address. This + /// constructor may be used for accepting connections on a specific interface + /// or for making a connection to a remote endpoint. + endpoint(unsigned short port_num, const boost::asio::ipv4::address& addr) + { + addr_.sin_family = AF_INET; + addr_.sin_port = + boost::asio::detail::socket_ops::host_to_network_short(port_num); + addr_.sin_addr.s_addr = + boost::asio::detail::socket_ops::host_to_network_long(addr.to_ulong()); + } + + /// Copy constructor. + endpoint(const endpoint& other) + : addr_(other.addr_) + { + } + + /// Assign from another endpoint. + endpoint& operator=(const endpoint& other) + { + addr_ = other.addr_; + return *this; + } + + /// The protocol associated with the endpoint. + protocol_type protocol() const + { + return protocol_type(); + } + + /// Get the underlying endpoint in the native type. + data_type* data() + { + return reinterpret_cast(&addr_); + } + + /// Get the underlying endpoint in the native type. + const data_type* data() const + { + return reinterpret_cast(&addr_); + } + + /// Get the underlying size of the endpoint in the native type. + size_type size() const + { + return sizeof(addr_); + } + + /// Set the underlying size of the endpoint in the native type. + void size(size_type size) + { + if (size != sizeof(addr_)) + { + boost::asio::error e(boost::asio::error::invalid_argument); + boost::throw_exception(e); + } + } + + /// Get the port associated with the endpoint. The port number is always in + /// the host's byte order. + unsigned short port() const + { + return boost::asio::detail::socket_ops::network_to_host_short( + addr_.sin_port); + } + + /// Set the port associated with the endpoint. The port number is always in + /// the host's byte order. + void port(unsigned short port_num) + { + addr_.sin_port = + boost::asio::detail::socket_ops::host_to_network_short(port_num); + } + + /// Get the IP address associated with the endpoint. + boost::asio::ipv4::address address() const + { + return boost::asio::detail::socket_ops::network_to_host_long( + addr_.sin_addr.s_addr); + } + + /// Set the IP address associated with the endpoint. + void address(const boost::asio::ipv4::address& addr) + { + addr_.sin_addr.s_addr = + boost::asio::detail::socket_ops::host_to_network_long(addr.to_ulong()); + } + + /// Compare two endpoints for equality. + friend bool operator==(const endpoint& e1, const endpoint& e2) + { + return e1.address() == e2.address() && e1.port() == e2.port(); + } + + /// Compare two endpoints for inequality. + friend bool operator!=(const endpoint& e1, const endpoint& e2) + { + return e1.address() != e2.address() || e1.port() != e2.port(); + } + + /// Compare endpoints for ordering. + friend bool operator<(const endpoint& e1, const endpoint& e2) + { + if (e1.address() < e2.address()) + return true; + if (e1.address() != e2.address()) + return false; + return e1.port() < e2.port(); + } + +private: + // The underlying IPv4 socket address. + boost::asio::detail::inet_addr_v4_type addr_; +}; + +/// Output an endpoint as a string. +/** + * Used to output a human-readable string for a specified endpoint. + * + * @param os The output stream to which the string will be written. + * + * @param endpoint The endpoint to be written. + * + * @return The output stream. + * + * @relates udp::endpoint + */ +#if BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564)) +std::ostream& operator<<(std::ostream& os, const udp::endpoint& endpoint) +{ + os << endpoint.address().to_string() << ':' << endpoint.port(); + return os; +} +#else // BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564)) +template +Ostream& operator<<(Ostream& os, const udp::endpoint& endpoint) +{ + os << endpoint.address().to_string() << ':' << endpoint.port(); + return os; +} +#endif // BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564)) + +} // namespace ipv4 +} // namespace asio +} // namespace boost + +#include + +#endif // BOOST_ASIO_IPV4_UDP_HPP diff --git a/include/boost/asio/is_read_buffered.hpp b/include/boost/asio/is_read_buffered.hpp new file mode 100644 index 00000000..37486b58 --- /dev/null +++ b/include/boost/asio/is_read_buffered.hpp @@ -0,0 +1,64 @@ +// +// is_read_buffered.hpp +// ~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2005 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_IS_READ_BUFFERED_HPP +#define BOOST_ASIO_IS_READ_BUFFERED_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include + +#include +#include +#include + +#include +#include + +namespace boost { +namespace asio { + +namespace detail { + +template +char is_read_buffered_helper(buffered_stream* s); + +template +char is_read_buffered_helper(buffered_read_stream* s); + +struct is_read_buffered_big_type { char data[10]; }; +is_read_buffered_big_type is_read_buffered_helper(...); + +} // namespace detail + +/// The is_read_buffered class is a traits class that may be used to determine +/// whether a stream type supports buffering of read data. +template +class is_read_buffered +{ +public: +#if defined(GENERATING_DOCUMENTATION) + /// The value member is true only if the Stream type supports buffering of + /// read data. + static const bool value; +#else + BOOST_STATIC_CONSTANT(bool, + value = sizeof(detail::is_read_buffered_helper((Stream*)0)) == 1); +#endif +}; + +} // namespace asio +} // namespace boost + +#include + +#endif // BOOST_ASIO_IS_READ_BUFFERED_HPP diff --git a/include/boost/asio/is_write_buffered.hpp b/include/boost/asio/is_write_buffered.hpp new file mode 100644 index 00000000..67dbe6d9 --- /dev/null +++ b/include/boost/asio/is_write_buffered.hpp @@ -0,0 +1,64 @@ +// +// is_write_buffered.hpp +// ~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2005 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_IS_WRITE_BUFFERED_HPP +#define BOOST_ASIO_IS_WRITE_BUFFERED_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include + +#include +#include +#include + +#include +#include + +namespace boost { +namespace asio { + +namespace detail { + +template +char is_write_buffered_helper(buffered_stream* s); + +template +char is_write_buffered_helper(buffered_write_stream* s); + +struct is_write_buffered_big_type { char data[10]; }; +is_write_buffered_big_type is_write_buffered_helper(...); + +} // namespace detail + +/// The is_write_buffered class is a traits class that may be used to determine +/// whether a stream type supports buffering of written data. +template +class is_write_buffered +{ +public: +#if defined(GENERATING_DOCUMENTATION) + /// The value member is true only if the Stream type supports buffering of + /// written data. + static const bool value; +#else + BOOST_STATIC_CONSTANT(bool, + value = sizeof(detail::is_write_buffered_helper((Stream*)0)) == 1); +#endif +}; + +} // namespace asio +} // namespace boost + +#include + +#endif // BOOST_ASIO_IS_WRITE_BUFFERED_HPP diff --git a/include/boost/asio/locking_dispatcher.hpp b/include/boost/asio/locking_dispatcher.hpp new file mode 100644 index 00000000..2e01879f --- /dev/null +++ b/include/boost/asio/locking_dispatcher.hpp @@ -0,0 +1,35 @@ +// +// locking_dispatcher.hpp +// ~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2005 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_LOCKING_DISPATCHER_HPP +#define BOOST_ASIO_LOCKING_DISPATCHER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include + +#include +#include + +namespace boost { +namespace asio { + +/// Typedef for the typical usage of locking_dispatcher. +typedef basic_locking_dispatcher > + locking_dispatcher; + +} // namespace asio +} // namespace boost + +#include + +#endif // BOOST_ASIO_LOCKING_DISPATCHER_HPP diff --git a/include/boost/asio/locking_dispatcher_service.hpp b/include/boost/asio/locking_dispatcher_service.hpp new file mode 100644 index 00000000..82dba350 --- /dev/null +++ b/include/boost/asio/locking_dispatcher_service.hpp @@ -0,0 +1,107 @@ +// +// locking_dispatcher_service.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2005 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_LOCKING_DISPATCHER_SERVICE_HPP +#define BOOST_ASIO_LOCKING_DISPATCHER_SERVICE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include + +#include +#include +#include + +#include +#include +#include +#include + +namespace boost { +namespace asio { + +/// Default service implementation for a locking dispatcher. +template > +class locking_dispatcher_service + : private noncopyable +{ +public: + /// The demuxer type for this service. + typedef basic_demuxer > demuxer_type; + +private: + // The type of the platform-specific implementation. + typedef detail::locking_dispatcher_service service_impl_type; + +public: + /// The type of the locking dispatcher. +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined impl_type; +#else + typedef typename service_impl_type::impl_type impl_type; +#endif + + /// Constructor. + explicit locking_dispatcher_service(demuxer_type& demuxer) + : service_impl_(demuxer.get_service(service_factory())) + { + } + + /// Get the demuxer associated with the service. + demuxer_type& demuxer() + { + return service_impl_.demuxer(); + } + + /// Return a null locking dispatcher implementation. + impl_type null() const + { + return service_impl_.null(); + } + + /// Create a new locking dispatcher implementation. + void create(impl_type& impl) + { + service_impl_.create(impl); + } + + /// Destroy a locking dispatcher implementation. + void destroy(impl_type& impl) + { + service_impl_.destroy(impl); + } + + /// Request a dispatcher to invoke the given handler. + template + void dispatch(impl_type& impl, Handler handler) + { + service_impl_.dispatch(impl, handler); + } + + /// Request a dispatcher to invoke the given handler and return immediately. + template + void post(impl_type& impl, Handler handler) + { + service_impl_.post(impl, handler); + } + +private: + // The service that provides the platform-specific implementation. + service_impl_type& service_impl_; +}; + +} // namespace asio +} // namespace boost + +#include + +#endif // BOOST_ASIO_LOCKING_DISPATCHER_SERVICE_HPP diff --git a/include/boost/asio/placeholders.hpp b/include/boost/asio/placeholders.hpp new file mode 100644 index 00000000..eae2cbb6 --- /dev/null +++ b/include/boost/asio/placeholders.hpp @@ -0,0 +1,70 @@ +// +// placeholders.hpp +// ~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2005 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_ARG_HPP +#define BOOST_ASIO_ARG_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include + +#include +#include +#include + +namespace boost { +namespace asio { + +namespace placeholders { + +namespace { + +#if defined(__BORLANDC__) + +static inline boost::arg<1> error() +{ + return boost::arg<1>(); +} + +static inline boost::arg<2> bytes_transferred() +{ + return boost::arg<2>(); +} + +#elif defined(_MSC_VER) && (_MSC_VER < 1400) + +static boost::arg<1> error; +static boost::arg<2> bytes_transferred; + +#else + +/// An argument placeholder, for use with @ref boost_bind, that corresponds to +/// the error argument of a handler for any of the asynchronous functions. +boost::arg<1> error; + +/// An argument placeholder, for use with @ref boost_bind, that corresponds to +/// the bytes_transferred argument of a handler for asynchronous functions such +/// as boost::asio::async_write or boost::asio::stream_socket::async_write_some. +boost::arg<2> bytes_transferred; + +#endif + +} // namespace + +} // namespace placeholders + +} // namespace asio +} // namespace boost + +#include + +#endif // BOOST_ASIO_ARG_HPP diff --git a/include/boost/asio/read.hpp b/include/boost/asio/read.hpp new file mode 100644 index 00000000..e6dc7dd7 --- /dev/null +++ b/include/boost/asio/read.hpp @@ -0,0 +1,326 @@ +// +// read.hpp +// ~~~~~~~~ +// +// Copyright (c) 2003-2005 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_READ_HPP +#define BOOST_ASIO_READ_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include + +#include +#include +#include +#include + +namespace boost { +namespace asio { + +/** + * @defgroup read boost::asio::read + */ +/*@{*/ + +/// Attempt to read a certain amount of data from a stream before returning. +/** + * This function is used to read a certain number of bytes of data from a + * stream. The call will block until one of the following conditions is true: + * + * @li The supplied buffers are full. That is, the bytes transferred is equal to + * the sum of the buffer sizes. + * + * @li An error occurred. + * + * This operation is implemented in terms of one or more calls to the stream's + * read_some function. + * + * @param s The stream from which the data is to be read. The type must support + * the Sync_Read_Stream concept. + * + * @param buffers One or more buffers into which the data will be read. The sum + * of the buffer sizes indicates the maximum number of bytes to read from the + * stream. + * + * @returns The number of bytes transferred. + * + * @throws Sync_Read_Stream::error_type Thrown on failure. + * + * @par Example: + * To read into a single data buffer use the @ref buffer function as follows: + * @code boost::asio::read(s, boost::asio::buffer(data, size)); @endcode + * See the @ref buffer documentation for information on reading into multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + * + * @note This overload is equivalent to calling: + * @code boost::asio::read( + * s, buffers, + * boost::asio::transfer_all(), + * boost::asio::throw_error()); @endcode + */ +template +std::size_t read(Sync_Read_Stream& s, const Mutable_Buffers& buffers); + +/// Attempt to read a certain amount of data from a stream before returning. +/** + * This function is used to read a certain number of bytes of data from a + * stream. The call will block until one of the following conditions is true: + * + * @li The supplied buffers are full. That is, the bytes transferred is equal to + * the sum of the buffer sizes. + * + * @li The completion_condition function object returns true. + * + * This operation is implemented in terms of one or more calls to the stream's + * read_some function. + * + * @param s The stream from which the data is to be read. The type must support + * the Sync_Read_Stream concept. + * + * @param buffers One or more buffers into which the data will be read. The sum + * of the buffer sizes indicates the maximum number of bytes to read from the + * stream. + * + * @param completion_condition The function object to be called to determine + * whether the read operation is complete. The signature of the function object + * must be: + * @code bool completion_condition( + * const Sync_Read_Stream::error_type& error, // Result of latest read_some + * // operation. + * + * std::size_t bytes_transferred // Number of bytes transferred + * // so far. + * ); @endcode + * A return value of true indicates that the read operation is complete. False + * indicates that further calls to the stream's read_some function are required. + * + * @returns The number of bytes transferred. + * + * @throws Sync_Read_Stream::error_type Thrown on failure. + * + * @par Example: + * To read into a single data buffer use the @ref buffer function as follows: + * @code boost::asio::read(s, boost::asio::buffer(data, size), + * boost::asio::transfer_at_least(32)); @endcode + * See the @ref buffer documentation for information on reading into multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + * + * @note This overload is equivalent to calling: + * @code boost::asio::read( + * s, buffers, completion_condition, + * boost::asio::throw_error()); @endcode + */ +template +std::size_t read(Sync_Read_Stream& s, const Mutable_Buffers& buffers, + Completion_Condition completion_condition); + +/// Attempt to read a certain amount of data from a stream before returning. +/** + * This function is used to read a certain number of bytes of data from a + * stream. The call will block until one of the following conditions is true: + * + * @li The supplied buffers are full. That is, the bytes transferred is equal to + * the sum of the buffer sizes. + * + * @li The completion_condition function object returns true. + * + * This operation is implemented in terms of one or more calls to the stream's + * read_some function. + * + * @param s The stream from which the data is to be read. The type must support + * the Sync_Read_Stream concept. + * + * @param buffers One or more buffers into which the data will be read. The sum + * of the buffer sizes indicates the maximum number of bytes to read from the + * stream. + * + * @param completion_condition The function object to be called to determine + * whether the read operation is complete. The signature of the function object + * must be: + * @code bool completion_condition( + * const Sync_Read_Stream::error_type& error, // Result of latest read_some + * // operation. + * + * std::size_t bytes_transferred // Number of bytes transferred + * // so far. + * ); @endcode + * A return value of true indicates that the read operation is complete. False + * indicates that further calls to the stream's read_some function are required. + * + * @param error_handler The handler to be called when an error occurs. Copies + * will be made of the handler as required. The function signature of the + * handler must be: + * @code void error_handler( + * const Sync_Read_Stream::error_type& error // Result of operation. + * ); @endcode + * The error handler is only called if the completion_condition indicates that + * the operation is complete. + * + * @returns The number of bytes read. If an error occurs, and the error handler + * does not throw an exception, returns the total number of bytes successfully + * transferred prior to the error. + */ +template +std::size_t read(Sync_Read_Stream& s, const Mutable_Buffers& buffers, + Completion_Condition completion_condition, Error_Handler error_handler); + +/*@}*/ +/** + * @defgroup async_read boost::asio::async_read + */ +/*@{*/ + +/// Start an asynchronous operation to read a certain amount of data from a +/// stream. +/** + * This function is used to asynchronously read a certain number of bytes of + * data from a stream. The function call always returns immediately. The + * asynchronous operation will continue until one of the following conditions is + * true: + * + * @li The supplied buffers are full. That is, the bytes transferred is equal to + * the sum of the buffer sizes. + * + * @li An error occurred. + * + * This operation is implemented in terms of one or more calls to the stream's + * async_read_some function. + * + * @param s The stream from which the data is to be read. The type must support + * the Async_Read_Stream concept. + * + * @param buffers One or more buffers into which the data will be read. The sum + * of the buffer sizes indicates the maximum number of bytes to read from the + * stream. Although the buffers object may be copied as necessary, ownership of + * the underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the handler is called. + * + * @param handler The handler to be called when the read operation completes. + * Copies will be made of the handler as required. The function signature of the + * handler must be: + * @code void handler( + * const Async_Read_Stream::error_type& error, // Result of operation. + * + * std::size_t bytes_transferred // Number of bytes copied into + * // the buffers. If an error + * // occurred, this will be the + * // number of bytes successfully + * // transferred prior to the + * // error. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation of + * the handler will be performed in a manner equivalent to using + * boost::asio::demuxer::post(). + * + * @par Example: + * To read into a single data buffer use the @ref buffer function as follows: + * @code + * boost::asio::async_read(s, boost::asio::buffer(data, size), handler); + * @endcode + * See the @ref buffer documentation for information on reading into multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + * + * @note This overload is equivalent to calling: + * @code boost::asio::async_read( + * s, buffers, + * boost::asio::transfer_all(), + * handler); @endcode + */ +template +void async_read(Async_Read_Stream& s, const Mutable_Buffers& buffers, + Handler handler); + +/// Start an asynchronous operation to read a certain amount of data from a +/// stream. +/** + * This function is used to asynchronously read a certain number of bytes of + * data from a stream. The function call always returns immediately. The + * asynchronous operation will continue until one of the following conditions is + * true: + * + * @li The supplied buffers are full. That is, the bytes transferred is equal to + * the sum of the buffer sizes. + * + * @li The completion_condition function object returns true. + * + * @param s The stream from which the data is to be read. The type must support + * the Async_Read_Stream concept. + * + * @param buffers One or more buffers into which the data will be read. The sum + * of the buffer sizes indicates the maximum number of bytes to read from the + * stream. Although the buffers object may be copied as necessary, ownership of + * the underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the handler is called. + * + * @param completion_condition The function object to be called to determine + * whether the read operation is complete. The signature of the function object + * must be: + * @code bool completion_condition( + * const Async_Read_Stream::error_type& error, // Result of latest read_some + * // operation. + * + * std::size_t bytes_transferred // Number of bytes transferred + * // so far. + * ); @endcode + * A return value of true indicates that the read operation is complete. False + * indicates that further calls to the stream's async_read_some function are + * required. + * + * @param handler The handler to be called when the read operation completes. + * Copies will be made of the handler as required. The function signature of the + * handler must be: + * @code void handler( + * const Async_Read_Stream::error_type& error, // Result of operation. + * + * std::size_t bytes_transferred // Number of bytes copied into + * // the buffers. If an error + * // occurred, this will be the + * // number of bytes successfully + * // transferred prior to the + * // error. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation of + * the handler will be performed in a manner equivalent to using + * boost::asio::demuxer::post(). + * + * @par Example: + * To read into a single data buffer use the @ref buffer function as follows: + * @code boost::asio::async_read(s, + * boost::asio::buffer(data, size), + * boost::asio::transfer_at_least(32), + * handler); @endcode + * See the @ref buffer documentation for information on reading into multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ +template +void async_read(Async_Read_Stream& s, const Mutable_Buffers& buffers, + Completion_Condition completion_condition, Handler handler); + +/*@}*/ + +} // namespace asio +} // namespace boost + +#include + +#include + +#endif // BOOST_ASIO_READ_HPP diff --git a/include/boost/asio/service_factory.hpp b/include/boost/asio/service_factory.hpp new file mode 100644 index 00000000..5cc84308 --- /dev/null +++ b/include/boost/asio/service_factory.hpp @@ -0,0 +1,41 @@ +// +// service_factory.hpp +// ~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2005 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_SERVICE_FACTORY_HPP +#define BOOST_ASIO_SERVICE_FACTORY_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include + +namespace boost { +namespace asio { + +/// This class may be specialised to provide custom service creation. +template +class service_factory +{ +public: + /// Create a service with the specified owner. + template + Service* create(Owner& owner) + { + return new Service(owner); + } +}; + +} // namespace asio +} // namespace boost + +#include + +#endif // BOOST_ASIO_SERVICE_FACTORY_HPP diff --git a/include/boost/asio/socket_acceptor.hpp b/include/boost/asio/socket_acceptor.hpp new file mode 100644 index 00000000..7ff76873 --- /dev/null +++ b/include/boost/asio/socket_acceptor.hpp @@ -0,0 +1,34 @@ +// +// socket_acceptor.hpp +// ~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2005 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_SOCKET_ACCEPTOR_HPP +#define BOOST_ASIO_SOCKET_ACCEPTOR_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include + +#include +#include + +namespace boost { +namespace asio { + +/// Typedef for the typical usage of socket_acceptor. +typedef basic_socket_acceptor > socket_acceptor; + +} // namespace asio +} // namespace boost + +#include + +#endif // BOOST_ASIO_SOCKET_ACCEPTOR_HPP diff --git a/include/boost/asio/socket_acceptor_service.hpp b/include/boost/asio/socket_acceptor_service.hpp new file mode 100644 index 00000000..6c1cb8bb --- /dev/null +++ b/include/boost/asio/socket_acceptor_service.hpp @@ -0,0 +1,181 @@ +// +// socket_acceptor_service.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2005 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_SOCKET_ACCEPTOR_SERVICE_HPP +#define BOOST_ASIO_SOCKET_ACCEPTOR_SERVICE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace boost { +namespace asio { + +/// Default service implementation for a socket acceptor. +template > +class socket_acceptor_service + : private noncopyable +{ +public: + /// The demuxer type. + typedef basic_demuxer > demuxer_type; + +private: + // The type of the platform-specific implementation. +#if defined(BOOST_ASIO_HAS_IOCP_DEMUXER) + typedef detail::win_iocp_socket_service service_impl_type; +#elif defined(BOOST_ASIO_HAS_EPOLL_REACTOR) + typedef detail::reactive_socket_service< + demuxer_type, detail::epoll_reactor > service_impl_type; +#elif defined(BOOST_ASIO_HAS_KQUEUE_REACTOR) + typedef detail::reactive_socket_service< + demuxer_type, detail::kqueue_reactor > service_impl_type; +#else + typedef detail::reactive_socket_service< + demuxer_type, detail::select_reactor > service_impl_type; +#endif + +public: + /// The native type of the socket acceptor. +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined impl_type; +#else + typedef typename service_impl_type::impl_type impl_type; +#endif + + /// Construct a new socket acceptor service for the specified demuxer. + explicit socket_acceptor_service(demuxer_type& demuxer) + : service_impl_(demuxer.get_service(service_factory())) + { + } + + /// Get the demuxer associated with the service. + demuxer_type& demuxer() + { + return service_impl_.demuxer(); + } + + /// Return a null socket acceptor implementation. + impl_type null() const + { + return service_impl_.null(); + } + + /// Open a new socket acceptor implementation. + template + void open(impl_type& impl, const Protocol& protocol, + Error_Handler error_handler) + { + service_impl_.open(impl, protocol, error_handler); + } + + /// Bind the socket acceptor to the specified local endpoint. + template + void bind(impl_type& impl, const Endpoint& endpoint, + Error_Handler error_handler) + { + service_impl_.bind(impl, endpoint, error_handler); + } + + /// Place the socket acceptor into the state where it will listen for new + /// connections. + template + void listen(impl_type& impl, int backlog, Error_Handler error_handler) + { + service_impl_.listen(impl, backlog, error_handler); + } + + /// Close a socket acceptor implementation. + template + void close(impl_type& impl, Error_Handler error_handler) + { + service_impl_.close(impl, error_handler); + } + + /// Set a socket option. + template + void set_option(impl_type& impl, const Option& option, + Error_Handler error_handler) + { + service_impl_.set_option(impl, option, error_handler); + } + + /// Set a socket option. + template + void get_option(impl_type& impl, Option& option, Error_Handler error_handler) + { + service_impl_.get_option(impl, option, error_handler); + } + + /// Get the local endpoint. + template + void get_local_endpoint(impl_type& impl, Endpoint& endpoint, + Error_Handler error_handler) + { + service_impl_.get_local_endpoint(impl, endpoint, error_handler); + } + + /// Accept a new connection. + template + void accept(impl_type& impl, Socket& peer, Error_Handler error_handler) + { + service_impl_.accept(impl, peer, error_handler); + } + + /// Accept a new connection. + template + void accept_endpoint(impl_type& impl, Socket& peer, Endpoint& peer_endpoint, + Error_Handler error_handler) + { + service_impl_.accept_endpoint(impl, peer, peer_endpoint, error_handler); + } + + /// Start an asynchronous accept. + template + void async_accept(impl_type& impl, Socket& peer, Handler handler) + { + service_impl_.async_accept(impl, peer, handler); + } + + /// Start an asynchronous accept. + template + void async_accept_endpoint(impl_type& impl, Socket& peer, + Endpoint& peer_endpoint, Handler handler) + { + service_impl_.async_accept_endpoint(impl, peer, peer_endpoint, handler); + } + +private: + // The service that provides the platform-specific implementation. + service_impl_type& service_impl_; +}; + +} // namespace asio +} // namespace boost + +#include + +#endif // BOOST_ASIO_SOCKET_ACCEPTOR_SERVICE_HPP diff --git a/include/boost/asio/socket_base.hpp b/include/boost/asio/socket_base.hpp new file mode 100644 index 00000000..034b3578 --- /dev/null +++ b/include/boost/asio/socket_base.hpp @@ -0,0 +1,440 @@ +// +// socket_base.hpp +// ~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2005 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_SOCKET_BASE_HPP +#define BOOST_ASIO_SOCKET_BASE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include + +#include +#include +#include +#include + +#include +#include +#include + +namespace boost { +namespace asio { + +/// The socket_base class is used as a base for the basic_stream_socket and +/// basic_datagram_socket class templates so that we have a common place to +/// define the shutdown_type and enum. +class socket_base +{ +public: + /// Different ways a socket may be shutdown. + enum shutdown_type + { +#if defined(GENERATING_DOCUMENTATION) + /// Shutdown the receive side of the socket. + shutdown_receive = implementation_defined, + + /// Shutdown the send side of the socket. + shutdown_send = implementation_defined, + + /// Shutdown both send and receive on the socket. + shutdown_both = implementation_defined +#else + shutdown_receive = boost::asio::detail::shutdown_receive, + shutdown_send = boost::asio::detail::shutdown_send, + shutdown_both = boost::asio::detail::shutdown_both +#endif + }; + + /// Bitmask type for flags that can be passed to send and receive operations. + typedef int message_flags; + +#if defined(GENERATING_DOCUMENTATION) + /// Peek at incoming data without removing it from the input queue. + static const int message_peek = implementation_defined; + + /// Process out-of-band data. + static const int message_out_of_band = implementation_defined; + + /// Specify that the data should not be subject to routing. + static const int message_do_not_route = implementation_defined; +#else + BOOST_STATIC_CONSTANT(int, + message_peek = boost::asio::detail::message_peek); + BOOST_STATIC_CONSTANT(int, + message_out_of_band = boost::asio::detail::message_out_of_band); + BOOST_STATIC_CONSTANT(int, + message_do_not_route = boost::asio::detail::message_do_not_route); +#endif + + /// Socket option to permit sending of broadcast messages. + /** + * Implements the SOL_SOCKET/SO_BROADCAST socket option. + * + * @par Examples: + * Setting the option: + * @code + * boost::asio::datagram_socket socket(demuxer); + * ... + * boost::asio::socket_base::broadcast option(true); + * socket.set_option(option); + * @endcode + * + * @par + * Getting the current option value: + * @code + * boost::asio::datagram_socket socket(demuxer); + * ... + * boost::asio::socket_base::broadcast option; + * socket.get_option(option); + * bool is_set = option.get(); + * @endcode + * + * @par Concepts: + * Socket_Option, Boolean_Socket_Option. + */ +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined broadcast; +#else + typedef boost::asio::detail::socket_option::boolean< + SOL_SOCKET, SO_BROADCAST> broadcast; +#endif + + /// Socket option to prevent routing, use local interfaces only. + /** + * Implements the SOL_SOCKET/SO_DONTROUTE socket option. + * + * @par Examples: + * Setting the option: + * @code + * boost::asio::datagram_socket socket(demuxer); + * ... + * boost::asio::socket_base::do_not_route option(true); + * socket.set_option(option); + * @endcode + * + * @par + * Getting the current option value: + * @code + * boost::asio::datagram_socket socket(demuxer); + * ... + * boost::asio::socket_base::do_not_route option; + * socket.get_option(option); + * bool is_set = option.get(); + * @endcode + * + * @par Concepts: + * Socket_Option, Boolean_Socket_Option. + */ +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined do_not_route; +#else + typedef boost::asio::detail::socket_option::boolean< + SOL_SOCKET, SO_DONTROUTE> do_not_route; +#endif + + /// Socket option to send keep-alives. + /** + * Implements the SOL_SOCKET/SO_KEEPALIVE socket option. + * + * @par Examples: + * Setting the option: + * @code + * boost::asio::stream_socket socket(demuxer); + * ... + * boost::asio::socket_base::keep_alive option(true); + * socket.set_option(option); + * @endcode + * + * @par + * Getting the current option value: + * @code + * boost::asio::stream_socket socket(demuxer); + * ... + * boost::asio::socket_base::keep_alive option; + * socket.get_option(option); + * bool is_set = option.get(); + * @endcode + * + * @par Concepts: + * Socket_Option, Boolean_Socket_Option. + */ +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined keep_alive; +#else + typedef boost::asio::detail::socket_option::boolean< + SOL_SOCKET, SO_KEEPALIVE> keep_alive; +#endif + + /// Socket option for the send buffer size of a socket. + /** + * Implements the SOL_SOCKET/SO_SNDBUF socket option. + * + * @par Examples: + * Setting the option: + * @code + * boost::asio::stream_socket socket(demuxer); + * ... + * boost::asio::socket_base::send_buffer_size option(8192); + * socket.set_option(option); + * @endcode + * + * @par + * Getting the current option value: + * @code + * boost::asio::stream_socket socket(demuxer); + * ... + * boost::asio::socket_base::send_buffer_size option; + * socket.get_option(option); + * int size = option.get(); + * @endcode + * + * @par Concepts: + * Socket_Option, Integer_Socket_Option. + */ +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined send_buffer_size; +#else + typedef boost::asio::detail::socket_option::integer< + SOL_SOCKET, SO_SNDBUF> send_buffer_size; +#endif + + /// Socket option for the send low watermark. + /** + * Implements the SOL_SOCKET/SO_SNDLOWAT socket option. + * + * @par Examples: + * Setting the option: + * @code + * boost::asio::stream_socket socket(demuxer); + * ... + * boost::asio::socket_base::send_low_watermark option(1024); + * socket.set_option(option); + * @endcode + * + * @par + * Getting the current option value: + * @code + * boost::asio::stream_socket socket(demuxer); + * ... + * boost::asio::socket_base::send_low_watermark option; + * socket.get_option(option); + * int size = option.get(); + * @endcode + * + * @par Concepts: + * Socket_Option, Integer_Socket_Option. + */ +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined send_low_watermark; +#else + typedef boost::asio::detail::socket_option::integer< + SOL_SOCKET, SO_SNDLOWAT> send_low_watermark; +#endif + + /// Socket option for the receive buffer size of a socket. + /** + * Implements the SOL_SOCKET/SO_RCVBUF socket option. + * + * @par Examples: + * Setting the option: + * @code + * boost::asio::stream_socket socket(demuxer); + * ... + * boost::asio::socket_base::receive_buffer_size option(8192); + * socket.set_option(option); + * @endcode + * + * @par + * Getting the current option value: + * @code + * boost::asio::stream_socket socket(demuxer); + * ... + * boost::asio::socket_base::receive_buffer_size option; + * socket.get_option(option); + * int size = option.get(); + * @endcode + * + * @par Concepts: + * Socket_Option, Integer_Socket_Option. + */ +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined receive_buffer_size; +#else + typedef boost::asio::detail::socket_option::integer< + SOL_SOCKET, SO_RCVBUF> receive_buffer_size; +#endif + + /// Socket option for the receive low watermark. + /** + * Implements the SOL_SOCKET/SO_RCVLOWAT socket option. + * + * @par Examples: + * Setting the option: + * @code + * boost::asio::stream_socket socket(demuxer); + * ... + * boost::asio::socket_base::receive_low_watermark option(1024); + * socket.set_option(option); + * @endcode + * + * @par + * Getting the current option value: + * @code + * boost::asio::stream_socket socket(demuxer); + * ... + * boost::asio::socket_base::receive_low_watermark option; + * socket.get_option(option); + * int size = option.get(); + * @endcode + * + * @par Concepts: + * Socket_Option, Integer_Socket_Option. + */ +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined receive_low_watermark; +#else + typedef boost::asio::detail::socket_option::integer< + SOL_SOCKET, SO_RCVLOWAT> receive_low_watermark; +#endif + + /// Socket option to allow the socket to be bound to an address that is + /// already in use. + /** + * Implements the SOL_SOCKET/SO_REUSEADDR socket option. + * + * @par Examples: + * Setting the option: + * @code + * boost::asio::socket_acceptor acceptor(demuxer); + * ... + * boost::asio::socket_base::reuse_address option(true); + * acceptor.set_option(option); + * @endcode + * + * @par + * Getting the current option value: + * @code + * boost::asio::socket_acceptor acceptor(demuxer); + * ... + * boost::asio::socket_base::reuse_address option; + * acceptor.get_option(option); + * bool is_set = option.get(); + * @endcode + * + * @par Concepts: + * Socket_Option, Boolean_Socket_Option. + */ +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined reuse_address; +#else + typedef boost::asio::detail::socket_option::boolean< + SOL_SOCKET, SO_REUSEADDR> reuse_address; +#endif + + /// Socket option to specify whether the socket lingers on close if unsent + /// data is present. + /** + * Implements the SOL_SOCKET/SO_LINGER socket option. + * + * @par Examples: + * Setting the option: + * @code + * boost::asio::stream_socket socket(demuxer); + * ... + * boost::asio::socket_base::linger option(true, 30); + * socket.set_option(option); + * @endcode + * + * @par + * Getting the current option value: + * @code + * boost::asio::stream_socket socket(demuxer); + * ... + * boost::asio::socket_base::linger option; + * socket.get_option(option); + * bool is_set = option.enabled(); + * unsigned short timeout = option.timeout(); + * @endcode + * + * @par Concepts: + * Socket_Option, Linger_Socket_Option. + */ +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined linger; +#else + typedef boost::asio::detail::socket_option::linger< + SOL_SOCKET, SO_LINGER> linger; +#endif + + /// IO control command to set the blocking mode of the socket. + /** + * Implements the FIONBIO IO control command. + * + * @par Example: + * @code + * boost::asio::stream_socket socket(demuxer); + * ... + * boost::asio::socket_base::non_blocking_io command(true); + * socket.io_control(command); + * @endcode + * + * @par Concepts: + * IO_Control_Command, Boolean_IO_Control_Command. + */ +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined non_blocking_io; +#else + typedef boost::asio::detail::io_control::boolean non_blocking_io; +#endif + + /// IO control command to get the amount of data that can be read without + /// blocking. + /** + * Implements the FIONREAD IO control command. + * + * @par Example: + * @code + * boost::asio::stream_socket socket(demuxer); + * ... + * boost::asio::socket_base::bytes_readable command(true); + * socket.io_control(command); + * std::size_t bytes_readable = command.get(); + * @endcode + * + * @par Concepts: + * IO_Control_Command, Size_IO_Control_Command. + */ +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined bytes_readable; +#else + typedef boost::asio::detail::io_control::size bytes_readable; +#endif + +protected: + /// Protected destructor to prevent deletion through this type. + ~socket_base() + { + } + +#if BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564)) +private: + // Workaround to enable the empty base optimisation with Borland C++. + char dummy_; +#endif +}; + +} // namespace asio +} // namespace boost + +#include + +#endif // BOOST_ASIO_SOCKET_BASE_HPP diff --git a/include/boost/asio/ssl.hpp b/include/boost/asio/ssl.hpp new file mode 100644 index 00000000..c3e7483a --- /dev/null +++ b/include/boost/asio/ssl.hpp @@ -0,0 +1,26 @@ +// +// ssl.hpp +// ~~~~~~~ +// +// Copyright (c) 2003-2005 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_SSL_HPP +#define BOOST_ASIO_SSL_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include +#include +#include +#include +#include +#include +#include + +#endif // BOOST_ASIO_SSL_HPP diff --git a/include/boost/asio/ssl/basic_context.hpp b/include/boost/asio/ssl/basic_context.hpp new file mode 100644 index 00000000..0e7c2b17 --- /dev/null +++ b/include/boost/asio/ssl/basic_context.hpp @@ -0,0 +1,424 @@ +// +// basic_context.hpp +// ~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2005 Voipster / Indrek dot Juhani at voipster dot com +// Copyright (c) 2005 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_SSL_BASIC_CONTEXT_HPP +#define BOOST_ASIO_SSL_BASIC_CONTEXT_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include + +#include +#include +#include +#include + +#include +#include +#include + +namespace boost { +namespace asio { +namespace ssl { + +/// SSL context. +template +class basic_context + : public context_base, + private boost::noncopyable +{ +public: + /// The type of the service that will be used to provide context operations. + typedef Service service_type; + + /// The native implementation type of the locking dispatcher. + typedef typename service_type::impl_type impl_type; + + /// The demuxer type for this context. + typedef typename service_type::demuxer_type demuxer_type; + + /// Constructor. + basic_context(demuxer_type& d, method m) + : service_(d.get_service(service_factory())), + impl_(service_.null()) + { + service_.create(impl_, m); + } + + /// Destructor. + ~basic_context() + { + service_.destroy(impl_); + } + + /// Get the underlying implementation in the native type. + /** + * This function may be used to obtain the underlying implementation of the + * context. This is intended to allow access to context functionality that is + * not otherwise provided. + */ + impl_type impl() + { + return impl_; + } + + /// Set options on the context. + /** + * This function may be used to configure the SSL options used by the context. + * + * @param o A bitmask of options. The available option values are defined in + * the context_base class. The options are bitwise-ored with any existing + * value for the options. + * + * @throws boost::asio::error Thrown on failure. + */ + void set_options(options o) + { + service_.set_options(impl_, o, throw_error()); + } + + /// Set options on the context. + /** + * This function may be used to configure the SSL options used by the context. + * + * @param o A bitmask of options. The available option values are defined in + * the context_base class. The options are bitwise-ored with any existing + * value for the options. + * + * @param error_handler The handler to be called when an error occurs. Copies + * will be made of the handler as required. The equivalent function signature + * of the handler must be: + * @code void error_handler( + * const boost::asio::error& error // Result of operation + * ); @endcode + */ + template + void set_options(options o, Error_Handler error_handler) + { + service_.set_options(impl_, o, error_handler); + } + + /// Set the peer verification mode. + /** + * This function may be used to configure the peer verification mode used by + * the context. + * + * @param v A bitmask of peer verification modes. The available verify_mode + * values are defined in the context_base class. + * + * @throws boost::asio::error Thrown on failure. + */ + void set_verify_mode(verify_mode v) + { + service_.set_verify_mode(impl_, v, throw_error()); + } + + /// Set the peer verification mode. + /** + * This function may be used to configure the peer verification mode used by + * the context. + * + * @param v A bitmask of peer verification modes. The available verify_mode + * values are defined in the context_base class. + * + * @param error_handler The handler to be called when an error occurs. Copies + * will be made of the handler as required. The equivalent function signature + * of the handler must be: + * @code void error_handler( + * const boost::asio::error& error // Result of operation + * ); @endcode + */ + template + void set_verify_mode(verify_mode v, Error_Handler error_handler) + { + service_.set_verify_mode(impl_, v, error_handler); + } + + /// Load a certification authority file for performing verification. + /** + * This function is used to load one or more trusted certification authorities + * from a file. + * + * @param filename The name of a file containing certification authority + * certificates in PEM format. + * + * @throws boost::asio::error Thrown on failure. + */ + void load_verify_file(const std::string& filename) + { + service_.load_verify_file(impl_, filename, throw_error()); + } + + /// Load a certification authority file for performing verification. + /** + * This function is used to load the certificates for one or more trusted + * certification authorities from a file. + * + * @param filename The name of a file containing certification authority + * certificates in PEM format. + * + * @param error_handler The handler to be called when an error occurs. Copies + * will be made of the handler as required. The equivalent function signature + * of the handler must be: + * @code void error_handler( + * const boost::asio::error& error // Result of operation + * ); @endcode + */ + template + void load_verify_file(const std::string& filename, + Error_Handler error_handler) + { + service_.load_verify_file(impl_, filename, error_handler); + } + + /// Add a directory containing certificate authority files to be used for + /// performing verification. + /** + * This function is used to specify the name of a directory containing + * certification authority certificates. Each file in the directory must + * contain a single certificate. The files must be named using the subject + * name's hash and an extension of ".0". + * + * @param path The name of a directory containing the certificates. + * + * @throws boost::asio::error Thrown on failure. + */ + void add_verify_path(const std::string& path) + { + service_.add_verify_path(impl_, path, throw_error()); + } + + /// Add a directory containing certificate authority files to be used for + /// performing verification. + /** + * This function is used to specify the name of a directory containing + * certification authority certificates. Each file in the directory must + * contain a single certificate. The files must be named using the subject + * name's hash and an extension of ".0". + * + * @param path The name of a directory containing the certificates. + * + * @param error_handler The handler to be called when an error occurs. Copies + * will be made of the handler as required. The equivalent function signature + * of the handler must be: + * @code void error_handler( + * const boost::asio::error& error // Result of operation + * ); @endcode + */ + template + void add_verify_path(const std::string& path, Error_Handler error_handler) + { + service_.add_verify_path(impl_, path, error_handler); + } + + /// Use a certificate from a file. + /** + * This function is used to load a certificate into the context from a file. + * + * @param filename The name of the file containing the certificate. + * + * @param format The file format (ASN.1 or PEM). + * + * @throws boost::asio::error Thrown on failure. + */ + void use_certificate_file(const std::string& filename, file_format format) + { + service_.use_certificate_file(impl_, filename, format, throw_error()); + } + + /// Use a certificate from a file. + /** + * This function is used to load a certificate into the context from a file. + * + * @param filename The name of the file containing the certificate. + * + * @param format The file format (ASN.1 or PEM). + * + * @param error_handler The handler to be called when an error occurs. Copies + * will be made of the handler as required. The equivalent function signature + * of the handler must be: + * @code void error_handler( + * const boost::asio::error& error // Result of operation + * ); @endcode + */ + template + void use_certificate_file(const std::string& filename, file_format format, + Error_Handler error_handler) + { + service_.use_certificate_file(impl_, filename, format, error_handler); + } + + /// Use a certificate chain from a file. + /** + * This function is used to load a certificate chain into the context from a + * file. + * + * @param filename The name of the file containing the certificate. The file + * must use the PEM format. + * + * @throws boost::asio::error Thrown on failure. + */ + void use_certificate_chain_file(const std::string& filename) + { + service_.use_certificate_chain_file(impl_, filename, throw_error()); + } + + /// Use a certificate chain from a file. + /** + * This function is used to load a certificate chain into the context from a + * file. + * + * @param filename The name of the file containing the certificate. The file + * must use the PEM format. + * + * @param error_handler The handler to be called when an error occurs. Copies + * will be made of the handler as required. The equivalent function signature + * of the handler must be: + * @code void error_handler( + * const boost::asio::error& error // Result of operation + * ); @endcode + */ + template + void use_certificate_chain_file(const std::string& filename, + Error_Handler error_handler) + { + service_.use_certificate_chain_file(impl_, filename, error_handler); + } + + /// Use a private key from a file. + /** + * This function is used to load a private key into the context from a file. + * + * @param filename The name of the file containing the private key. + * + * @param format The file format (ASN.1 or PEM). + * + * @throws boost::asio::error Thrown on failure. + */ + void use_private_key_file(const std::string& filename, file_format format) + { + service_.use_private_key_file(impl_, filename, format, throw_error()); + } + + /// Use a private key from a file. + /** + * This function is used to load a private key into the context from a file. + * + * @param filename The name of the file containing the private key. + * + * @param format The file format (ASN.1 or PEM). + * + * @param error_handler The handler to be called when an error occurs. Copies + * will be made of the handler as required. The equivalent function signature + * of the handler must be: + * @code void error_handler( + * const boost::asio::error& error // Result of operation + * ); @endcode + */ + template + void use_private_key_file(const std::string& filename, file_format format, + Error_Handler error_handler) + { + service_.use_private_key_file(impl_, filename, format, error_handler); + } + + /// Use an RSA private key from a file. + /** + * This function is used to load an RSA private key into the context from a + * file. + * + * @param filename The name of the file containing the RSA private key. + * + * @param format The file format (ASN.1 or PEM). + * + * @throws boost::asio::error Thrown on failure. + */ + void use_rsa_private_key_file(const std::string& filename, file_format format) + { + service_.use_rsa_private_key_file(impl_, filename, format, throw_error()); + } + + /// Use an RSA private key from a file. + /** + * This function is used to load an RSA private key into the context from a + * file. + * + * @param filename The name of the file containing the RSA private key. + * + * @param format The file format (ASN.1 or PEM). + * + * @param error_handler The handler to be called when an error occurs. Copies + * will be made of the handler as required. The equivalent function signature + * of the handler must be: + * @code void error_handler( + * const boost::asio::error& error // Result of operation + * ); @endcode + */ + template + void use_rsa_private_key_file(const std::string& filename, file_format format, + Error_Handler error_handler) + { + service_.use_rsa_private_key_file(impl_, filename, format, error_handler); + } + + /// Use the specified file to obtain the temporary Diffie-Hellman parameters. + /** + * This function is used to load Diffie-Hellman parameters into the context + * from a file. + * + * @param filename The name of the file containing the Diffie-Hellman + * parameters. The file must use the PEM format. + * + * @throws boost::asio::error Thrown on failure. + */ + void use_tmp_dh_file(const std::string& filename) + { + service_.use_tmp_dh_file(impl_, filename, throw_error()); + } + + /// Use the specified file to obtain the temporary Diffie-Hellman parameters. + /** + * This function is used to load Diffie-Hellman parameters into the context + * from a file. + * + * @param filename The name of the file containing the Diffie-Hellman + * parameters. The file must use the PEM format. + * + * @param error_handler The handler to be called when an error occurs. Copies + * will be made of the handler as required. The equivalent function signature + * of the handler must be: + * @code void error_handler( + * const boost::asio::error& error // Result of operation + * ); @endcode + */ + template + void use_tmp_dh_file(const std::string& filename, Error_Handler error_handler) + { + service_.use_tmp_dh_file(impl_, filename, error_handler); + } + +private: + /// The backend service implementation. + service_type& service_; + + /// The underlying native implementation. + impl_type impl_; +}; + +} // namespace ssl +} // namespace asio +} // namespace boost + +#include + +#endif // BOOST_ASIO_SSL_BASIC_CONTEXT_HPP diff --git a/include/boost/asio/ssl/context.hpp b/include/boost/asio/ssl/context.hpp new file mode 100644 index 00000000..86f0c628 --- /dev/null +++ b/include/boost/asio/ssl/context.hpp @@ -0,0 +1,37 @@ +// +// context.hpp +// ~~~~~~~~~~~ +// +// Copyright (c) 2005 Voipster / Indrek dot Juhani at voipster dot com +// Copyright (c) 2005 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_SSL_CONTEXT_HPP +#define BOOST_ASIO_SSL_CONTEXT_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include + +#include +#include + +namespace boost { +namespace asio { +namespace ssl { + +/// Typedef for the typical usage of context. +typedef basic_context > context; + +} // namespace ssl +} // namespace asio +} // namespace boost + +#include + +#endif // BOOST_ASIO_SSL_CONTEXT_HPP diff --git a/include/boost/asio/ssl/context_base.hpp b/include/boost/asio/ssl/context_base.hpp new file mode 100644 index 00000000..cb94a76d --- /dev/null +++ b/include/boost/asio/ssl/context_base.hpp @@ -0,0 +1,156 @@ +// +// context_base.hpp +// ~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2005 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_SSL_CONTEXT_BASE_HPP +#define BOOST_ASIO_SSL_CONTEXT_BASE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include + +#include +#include +#include +#include + +#include + +namespace boost { +namespace asio { +namespace ssl { + +/// The context_base class is used as a base for the basic_context class +/// template so that we have a common place to define various enums. +class context_base +{ +public: + /// Different methods supported by a context. + enum method + { + /// Generic SSL version 2. + sslv2, + + /// SSL version 2 client. + sslv2_client, + + /// SSL version 2 server. + sslv2_server, + + /// Generic SSL version 3. + sslv3, + + /// SSL version 3 client. + sslv3_client, + + /// SSL version 3 server. + sslv3_server, + + /// Generic TLS version 1. + tlsv1, + + /// TLS version 1 client. + tlsv1_client, + + /// TLS version 1 server. + tlsv1_server, + + /// Generic SSL/TLS. + sslv23, + + /// SSL/TLS client. + sslv23_client, + + /// SSL/TLS server. + sslv23_server + }; + + /// Bitmask type for SSL options. + typedef int options; + +#if defined(GENERATING_DOCUMENTATION) + /// Implement various bug workarounds. + static const int default_workarounds = implementation_defined; + + /// Always create a new key when using tmp_dh parameters. + static const int single_dh_use = implementation_defined; + + /// Disable SSL v2. + static const int no_sslv2 = implementation_defined; + + /// Disable SSL v3. + static const int no_sslv3 = implementation_defined; + + /// Disable TLS v1. + static const int no_tlsv1 = implementation_defined; +#else + BOOST_STATIC_CONSTANT(int, default_workarounds = SSL_OP_ALL); + BOOST_STATIC_CONSTANT(int, single_dh_use = SSL_OP_SINGLE_DH_USE); + BOOST_STATIC_CONSTANT(int, no_sslv2 = SSL_OP_NO_SSLv2); + BOOST_STATIC_CONSTANT(int, no_sslv3 = SSL_OP_NO_SSLv3); + BOOST_STATIC_CONSTANT(int, no_tlsv1 = SSL_OP_NO_TLSv1); +#endif + + /// File format types. + enum file_format + { + /// ASN.1 file. + asn1, + + /// PEM file. + pem + }; + + /// Bitmask type for peer verification. + typedef int verify_mode; + +#if defined(GENERATING_DOCUMENTATION) + /// No verification. + static const int verify_none = implementation_defined; + + /// Verify the peer. + static const int verify_peer = implementation_defined; + + /// Fail verification if the peer has no certificate. Ignored unless + /// verify_peer is set. + static const int verify_fail_if_no_peer_cert = implementation_defined; + + /// Do not request client certificate on renegotiation. Ignored unless + /// verify_peer is set. + static const int verify_client_once = implementation_defined; +#else + BOOST_STATIC_CONSTANT(int, verify_none = SSL_VERIFY_NONE); + BOOST_STATIC_CONSTANT(int, verify_peer = SSL_VERIFY_PEER); + BOOST_STATIC_CONSTANT(int, + verify_fail_if_no_peer_cert = SSL_VERIFY_FAIL_IF_NO_PEER_CERT); + BOOST_STATIC_CONSTANT(int, verify_client_once = SSL_VERIFY_CLIENT_ONCE); +#endif + +protected: + /// Protected destructor to prevent deletion through this type. + ~context_base() + { + } + +#if BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564)) +private: + // Workaround to enable the empty base optimisation with Borland C++. + char dummy_; +#endif +}; + +} // namespace ssl +} // namespace asio +} // namespace boost + +#include + +#endif // BOOST_ASIO_SSL_CONTEXT_BASE_HPP diff --git a/include/boost/asio/ssl/context_service.hpp b/include/boost/asio/ssl/context_service.hpp new file mode 100644 index 00000000..4b967dcb --- /dev/null +++ b/include/boost/asio/ssl/context_service.hpp @@ -0,0 +1,172 @@ +// +// context_service.hpp +// ~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2005 Voipster / Indrek dot Juhani at voipster dot com +// Copyright (c) 2005 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_SSL_CONTEXT_SERVICE_HPP +#define BOOST_ASIO_SSL_CONTEXT_SERVICE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace boost { +namespace asio { +namespace ssl { + +/// Default service implementation for a context. +template > +class context_service + : private boost::noncopyable +{ +public: + /// The demuxer type for this service. + typedef basic_demuxer > demuxer_type; + +private: + // The type of the platform-specific implementation. + typedef detail::openssl_context_service service_impl_type; + +public: + /// The type of the context. +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined impl_type; +#else + typedef typename service_impl_type::impl_type impl_type; +#endif + + /// Constructor. + explicit context_service(demuxer_type& demuxer) + : service_impl_(demuxer.get_service(service_factory())) + { + } + + /// Get the demuxer associated with the service. + demuxer_type& demuxer() + { + return service_impl_.demuxer(); + } + + /// Return a null context implementation. + impl_type null() const + { + return service_impl_.null(); + } + + /// Create a new context implementation. + void create(impl_type& impl, context_base::method m) + { + service_impl_.create(impl, m); + } + + /// Destroy a context implementation. + void destroy(impl_type& impl) + { + service_impl_.destroy(impl); + } + + /// Set options on the context. + template + void set_options(impl_type& impl, context_base::options o, + Error_Handler error_handler) + { + service_impl_.set_options(impl, o, error_handler); + } + + /// Set peer verification mode. + template + void set_verify_mode(impl_type& impl, context_base::verify_mode v, + Error_Handler error_handler) + { + service_impl_.set_verify_mode(impl, v, error_handler); + } + + /// Load a certification authority file for performing verification. + template + void load_verify_file(impl_type& impl, const std::string& filename, + Error_Handler error_handler) + { + service_impl_.load_verify_file(impl, filename, error_handler); + } + + /// Add a directory containing certification authority files to be used for + /// performing verification. + template + void add_verify_path(impl_type& impl, const std::string& path, + Error_Handler error_handler) + { + service_impl_.add_verify_path(impl, path, error_handler); + } + + /// Use a certificate from a file. + template + void use_certificate_file(impl_type& impl, const std::string& filename, + context_base::file_format format, Error_Handler error_handler) + { + service_impl_.use_certificate_file(impl, filename, format, error_handler); + } + + /// Use a certificate chain from a file. + template + void use_certificate_chain_file(impl_type& impl, const std::string& filename, + Error_Handler error_handler) + { + service_impl_.use_certificate_chain_file(impl, filename, error_handler); + } + + /// Use a private key from a file. + template + void use_private_key_file(impl_type& impl, const std::string& filename, + context_base::file_format format, Error_Handler error_handler) + { + service_impl_.use_private_key_file(impl, filename, format, error_handler); + } + + /// Use an RSA private key from a file. + template + void use_rsa_private_key_file(impl_type& impl, const std::string& filename, + context_base::file_format format, Error_Handler error_handler) + { + service_impl_.use_rsa_private_key_file(impl, filename, format, + error_handler); + } + + /// Use the specified file to obtain the temporary Diffie-Hellman parameters. + template + void use_tmp_dh_file(impl_type& impl, const std::string& filename, + Error_Handler error_handler) + { + service_impl_.use_tmp_dh_file(impl, filename, error_handler); + } + +private: + // The service that provides the platform-specific implementation. + service_impl_type& service_impl_; +}; + +} // namespace ssl +} // namespace asio +} // namespace boost + +#include + +#endif // BOOST_ASIO_SSL_CONTEXT_SERVICE_HPP diff --git a/include/boost/asio/ssl/detail/openssl_context_service.hpp b/include/boost/asio/ssl/detail/openssl_context_service.hpp new file mode 100644 index 00000000..9bfccaa5 --- /dev/null +++ b/include/boost/asio/ssl/detail/openssl_context_service.hpp @@ -0,0 +1,309 @@ +// +// openssl_context_service.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2005 Voipster / Indrek dot Juhani at voipster dot com +// Copyright (c) 2005 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_SSL_DETAIL_OPENSSL_CONTEXT_SERVICE_HPP +#define BOOST_ASIO_SSL_DETAIL_OPENSSL_CONTEXT_SERVICE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include + +#include +#include +#include + +#include +#include +#include +#include + +namespace boost { +namespace asio { +namespace ssl { +namespace detail { + +template +class openssl_context_service +{ +public: + // The native type of the context. + typedef ::SSL_CTX* impl_type; + + // Constructor. + openssl_context_service(Demuxer& d) + : demuxer_(d) + { + } + + // The demuxer type for this service. + typedef Demuxer demuxer_type; + + // Get the demuxer associated with the service. + demuxer_type& demuxer() + { + return demuxer_; + } + + // Return a null context implementation. + static impl_type null() + { + return 0; + } + + // Create a new context implementation. + void create(impl_type& impl, context_base::method m) + { + ::SSL_METHOD* ssl_method = 0; + switch (m) + { + case context_base::sslv2: + ssl_method = ::SSLv2_method(); + break; + case context_base::sslv2_client: + ssl_method = ::SSLv2_client_method(); + break; + case context_base::sslv2_server: + ssl_method = ::SSLv2_server_method(); + break; + case context_base::sslv3: + ssl_method = ::SSLv3_method(); + break; + case context_base::sslv3_client: + ssl_method = ::SSLv3_client_method(); + break; + case context_base::sslv3_server: + ssl_method = ::SSLv3_server_method(); + break; + case context_base::tlsv1: + ssl_method = ::TLSv1_method(); + break; + case context_base::tlsv1_client: + ssl_method = ::TLSv1_client_method(); + break; + case context_base::tlsv1_server: + ssl_method = ::TLSv1_server_method(); + break; + case context_base::sslv23: + ssl_method = ::SSLv23_method(); + break; + case context_base::sslv23_client: + ssl_method = ::SSLv23_client_method(); + break; + case context_base::sslv23_server: + ssl_method = ::SSLv23_server_method(); + break; + default: + break; + } + impl = ::SSL_CTX_new(ssl_method); + } + + // Destroy a context implementation. + void destroy(impl_type& impl) + { + if (impl != null()) + { + ::SSL_CTX_free(impl); + impl = null(); + } + } + + // Set options on the context. + template + void set_options(impl_type& impl, context_base::options o, + Error_Handler error_handler) + { + ::SSL_CTX_set_options(impl, o); + } + + // Set peer verification mode. + template + void set_verify_mode(impl_type& impl, context_base::verify_mode v, + Error_Handler error_handler) + { + ::SSL_CTX_set_verify(impl, v, 0); + } + + // Load a certification authority file for performing verification. + template + void load_verify_file(impl_type& impl, const std::string& filename, + Error_Handler error_handler) + { + if (::SSL_CTX_load_verify_locations(impl, filename.c_str(), 0) != 1) + { + boost::asio::error e(boost::asio::error::invalid_argument); + error_handler(e); + } + } + + // Add a directory containing certification authority files to be used for + // performing verification. + template + void add_verify_path(impl_type& impl, const std::string& path, + Error_Handler error_handler) + { + if (::SSL_CTX_load_verify_locations(impl, 0, path.c_str()) != 1) + { + boost::asio::error e(boost::asio::error::invalid_argument); + error_handler(e); + } + } + + // Use a certificate from a file. + template + void use_certificate_file(impl_type& impl, const std::string& filename, + context_base::file_format format, Error_Handler error_handler) + { + int file_type; + switch (format) + { + case context_base::asn1: + file_type = SSL_FILETYPE_ASN1; + break; + case context_base::pem: + file_type = SSL_FILETYPE_PEM; + break; + default: + { + boost::asio::error e(boost::asio::error::invalid_argument); + error_handler(e); + return; + } + } + + if (::SSL_CTX_use_certificate_file(impl, filename.c_str(), file_type) != 1) + { + boost::asio::error e(boost::asio::error::invalid_argument); + error_handler(e); + } + } + + // Use a certificate chain from a file. + template + void use_certificate_chain_file(impl_type& impl, const std::string& filename, + Error_Handler error_handler) + { + if (::SSL_CTX_use_certificate_chain_file(impl, filename.c_str()) != 1) + { + boost::asio::error e(boost::asio::error::invalid_argument); + error_handler(e); + } + } + + // Use a private key from a file. + template + void use_private_key_file(impl_type& impl, const std::string& filename, + context_base::file_format format, Error_Handler error_handler) + { + int file_type; + switch (format) + { + case context_base::asn1: + file_type = SSL_FILETYPE_ASN1; + break; + case context_base::pem: + file_type = SSL_FILETYPE_PEM; + break; + default: + { + boost::asio::error e(boost::asio::error::invalid_argument); + error_handler(e); + return; + } + } + + if (::SSL_CTX_use_PrivateKey_file(impl, filename.c_str(), file_type) != 1) + { + boost::asio::error e(boost::asio::error::invalid_argument); + error_handler(e); + } + } + + // Use an RSA private key from a file. + template + void use_rsa_private_key_file(impl_type& impl, const std::string& filename, + context_base::file_format format, Error_Handler error_handler) + { + int file_type; + switch (format) + { + case context_base::asn1: + file_type = SSL_FILETYPE_ASN1; + break; + case context_base::pem: + file_type = SSL_FILETYPE_PEM; + break; + default: + { + boost::asio::error e(boost::asio::error::invalid_argument); + error_handler(e); + return; + } + } + + if (::SSL_CTX_use_RSAPrivateKey_file( + impl, filename.c_str(), file_type) != 1) + { + boost::asio::error e(boost::asio::error::invalid_argument); + error_handler(e); + } + } + + // Use the specified file to obtain the temporary Diffie-Hellman parameters. + template + void use_tmp_dh_file(impl_type& impl, const std::string& filename, + Error_Handler error_handler) + { + ::BIO* bio = ::BIO_new_file(filename.c_str(), "r"); + if (!bio) + { + boost::asio::error e(boost::asio::error::invalid_argument); + error_handler(e); + return; + } + + ::DH* dh = ::PEM_read_bio_DHparams(bio, 0, 0, 0); + if (!dh) + { + ::BIO_free(bio); + boost::asio::error e(boost::asio::error::invalid_argument); + error_handler(e); + return; + } + + ::BIO_free(bio); + int result = ::SSL_CTX_set_tmp_dh(impl, dh); + if (result != 1) + { + ::DH_free(dh); + boost::asio::error e(boost::asio::error::invalid_argument); + error_handler(e); + } + } + +private: + // The demuxer that owns the service. + Demuxer& demuxer_; + + // Ensure openssl is initialised. + openssl_init<> init_; +}; + +} // namespace detail +} // namespace ssl +} // namespace asio +} // namespace boost + +#include + +#endif // BOOST_ASIO_SSL_DETAIL_OPENSSL_CONTEXT_SERVICE_HPP diff --git a/include/boost/asio/ssl/detail/openssl_init.hpp b/include/boost/asio/ssl/detail/openssl_init.hpp new file mode 100644 index 00000000..66002255 --- /dev/null +++ b/include/boost/asio/ssl/detail/openssl_init.hpp @@ -0,0 +1,124 @@ +// +// openssl_init.hpp +// ~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2005 Voipster / Indrek dot Juhani at voipster dot com +// Copyright (c) 2005 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_SSL_DETAIL_OPENSSL_INIT_HPP +#define BOOST_ASIO_SSL_DETAIL_OPENSSL_INIT_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include + +#include +#include +#include +#include + +#include +#include + +namespace boost { +namespace asio { +namespace ssl { +namespace detail { + +template +class openssl_init + : private boost::noncopyable +{ +private: + // Structure to perform the actual initialisation. + class do_init + { + public: + do_init() + { + if (Do_Init) + { + ::SSL_library_init(); + ::SSL_load_error_strings(); + + mutexes_.resize(::CRYPTO_num_locks()); + for (size_t i = 0; i < mutexes_.size(); ++i) + mutexes_[i].reset(new boost::asio::detail::mutex); + ::CRYPTO_set_locking_callback(&do_init::openssl_locking_func); + + ::OpenSSL_add_ssl_algorithms(); + } + } + + ~do_init() + { + if (Do_Init) + { + ::CRYPTO_set_locking_callback(0); + } + } + + // Helper function to manage a do_init singleton. The static instance of the + // openssl_init object ensures that this function is always called before + // main, and therefore before any other threads can get started. The do_init + // instance must be static in this function to ensure that it gets + // initialised before any other global objects try to use it. + static boost::shared_ptr instance() + { + static boost::shared_ptr init(new do_init); + return init; + } + + private: + static void openssl_locking_func(int mode, int n, + const char *file, int line) + { + if (mode & CRYPTO_LOCK) + instance()->mutexes_[n]->lock(); + else + instance()->mutexes_[n]->unlock(); + } + + // Mutexes to be used in locking callbacks. + std::vector > mutexes_; + }; + +public: + // Constructor. + openssl_init() + : ref_(do_init::instance()) + { + while (&instance_ == 0); // Ensure openssl_init::instance_ is linked in. + } + + // Destructor. + ~openssl_init() + { + } + +private: + // Instance to force initialisation of openssl at global scope. + static openssl_init instance_; + + // Reference to singleton do_init object to ensure that openssl does not get + // cleaned up until the last user has finished with it. + boost::shared_ptr ref_; +}; + +template +openssl_init openssl_init::instance_; + +} // namespace detail +} // namespace ssl +} // namespace asio +} // namespace boost + +#include + +#endif // BOOST_ASIO_SSL_DETAIL_OPENSSL_INIT_HPP diff --git a/include/boost/asio/ssl/detail/openssl_operation.hpp b/include/boost/asio/ssl/detail/openssl_operation.hpp new file mode 100644 index 00000000..5cb0fc8a --- /dev/null +++ b/include/boost/asio/ssl/detail/openssl_operation.hpp @@ -0,0 +1,405 @@ +// +// openssl_operation.hpp +// ~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2005 Voipster / Indrek dot Juhani at voipster dot com +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_SSL_DETAIL_OPENSSL_OPERATION_HPP +#define BOOST_ASIO_SSL_DETAIL_OPENSSL_OPERATION_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace boost { +namespace asio { +namespace ssl { +namespace detail { + +typedef boost::function ssl_primitive_func; +typedef boost::function user_handler_func; + +// Network send_/recv buffer implementation +// +// +class net_buffer +{ + static const int NET_BUF_SIZE = 16*1024 + 256; // SSL record size + spare + + unsigned char buf_[NET_BUF_SIZE]; + unsigned char* data_start_; + unsigned char* data_end_; + +public: + net_buffer() + { + data_start_ = data_end_ = buf_; + } + unsigned char* get_unused_start() { return data_end_; } + unsigned char* get_data_start() { return data_start_; } + size_t get_unused_len() { return (NET_BUF_SIZE - (data_end_ - buf_)); } + size_t get_data_len() { return (data_end_ - data_start_); } + void data_added(size_t count) + { + data_end_ += count; + data_end_ = data_end_ > (buf_ + NET_BUF_SIZE)? + (buf_ + NET_BUF_SIZE): + data_end_; + } + void data_removed(size_t count) + { + data_start_ += count; + if (data_start_ >= data_end_) reset(); + } + void reset() { data_start_ = buf_; data_end_ = buf_; } + bool has_data() { return (data_start_ < data_end_); } +}; // class net_buffer + +// +// Operation class +// +// +template +class openssl_operation +{ +public: + + // Constructor for asynchronous operations + openssl_operation(ssl_primitive_func primitive, + Stream& socket, + SSL* session, + BIO* ssl_bio, + user_handler_func handler + ) + : primitive_(primitive) + , user_handler_(handler) + , socket_(socket) + , ssl_bio_(ssl_bio) + , session_(session) + { + write_ = boost::bind( + &openssl_operation::do_async_write, + this, boost::arg<1>(), boost::arg<2>() + ); + handler_= boost::bind( + &openssl_operation::async_user_handler, + this, boost::arg<1>(), boost::arg<2>() + ); + } + + // Constructor for synchronous operations + openssl_operation(ssl_primitive_func primitive, + Stream& socket, + SSL* session, + BIO* ssl_bio) + : primitive_(primitive) + , socket_(socket) + , ssl_bio_(ssl_bio) + , session_(session) + { + write_ = boost::bind( + &openssl_operation::do_sync_write, + this, boost::arg<1>(), boost::arg<2>() + ); + handler_ = boost::bind( + &openssl_operation::sync_user_handler, + this, boost::arg<1>(), boost::arg<2>() + ); + } + + // Start operation + // In case of asynchronous it returns 0, in sync mode returns success code + // or throws an error... + int start() + { + int rc = primitive_( session_ ); + bool is_operation_done = (rc > 0); + // For connect/accept/shutdown, the operation + // is done, when return code is 1 + // for write, it is done, when is retcode > 0 + // for read, is is done when retcode > 0 + + int error_code = !is_operation_done ? + ::SSL_get_error( session_, rc ) : + 0; + bool is_read_needed = (error_code == SSL_ERROR_WANT_READ); + bool is_write_needed = (error_code == SSL_ERROR_WANT_WRITE || + ::BIO_ctrl_pending( ssl_bio_ )); + bool is_shut_down_received = + ((::SSL_get_shutdown( session_ ) & SSL_RECEIVED_SHUTDOWN) == + SSL_RECEIVED_SHUTDOWN); + bool is_shut_down_sent = + ((::SSL_get_shutdown( session_ ) & SSL_SENT_SHUTDOWN) == + SSL_SENT_SHUTDOWN); + + if (is_shut_down_sent && is_shut_down_received && is_operation_done) + // SSL connection is shut down cleanly + return handler_(boost::asio::error(), 1); + + if (is_shut_down_received) + // Shutdown has been requested, while we were reading or writing... + // abort our action... + return handler_(boost::asio::error(boost::asio::error::shut_down), 0); + + if (!is_operation_done && !is_read_needed && !is_write_needed + && !is_shut_down_sent) + // The operation has failed... It is not completed and does + // not want network communication nor does want to send shutdown out... + return handler_(boost::asio::error(error_code), rc); + + // Continue with operation, flush any SSL data out to network... + return write_(is_operation_done, rc); + } + +// Private implementation +private: + typedef boost::function + int_handler_func; + typedef boost::function write_func; + + ssl_primitive_func primitive_; + user_handler_func user_handler_; + write_func write_; + int_handler_func handler_; + + net_buffer send_buf_; // buffers for network IO + net_buffer recv_buf_; + + Stream& socket_; + BIO* ssl_bio_; + SSL* session_; + + // + int sync_user_handler(const boost::asio::error& error, int rc) + { + if (!error) + return rc; + + throw error; + } + + int async_user_handler(const boost::asio::error& error, int rc) + { + user_handler_(error, rc); + return 0; + } + + // Writes bytes asynchronously from SSL to NET + int do_async_write(bool is_operation_done, int rc) + { + int len = ::BIO_ctrl_pending( ssl_bio_ ); + if ( len ) + { + // There is something to write into net, do it... + len = (int)send_buf_.get_unused_len() > len? + len: + send_buf_.get_unused_len(); + + if (len == 0) + // In case our send buffer is full, we have just to wait until + // previous send to complete... + return 0; + + // Read outgoing data from bio + len = ::BIO_read( ssl_bio_, send_buf_.get_unused_start(), len); + + if (len > 0) + { + boost::asio::async_write + ( + socket_, + boost::asio::buffer(send_buf_.get_unused_start(), len), + boost::bind + ( + &openssl_operation::async_write_handler, + this, + is_operation_done, + rc, + boost::asio::placeholders::error, + boost::asio::placeholders::bytes_transferred + ) + ); + + send_buf_.data_added(len); + return 0; + } + else + // Seems like fatal error + // reading from SSL BIO has failed... + handler_(boost::asio::error(boost::asio::error::no_recovery), 0); + } + + if (is_operation_done) + { + // Finish the operation, with success + handler_(boost::asio::error(), rc); + return 0; + } + + // OPeration is not done and writing to net has been made... + // start reading... + do_async_read(); + + return 0; + } + + void async_write_handler(bool is_operation_done, int rc, + const boost::asio::error& error, size_t bytes_sent) + { + if (!error) + { + // Remove data from send buffer + send_buf_.data_removed(bytes_sent); + + if (is_operation_done) + handler_(boost::asio::error(), rc); + else + // Since the operation was not completed, try it again... + start(); + } + else + handler_(error, rc); + } + + void do_async_read() + { + // Wait for new data + socket_.async_read_some + ( + boost::asio::buffer(recv_buf_.get_unused_start(), + recv_buf_.get_unused_len()), + boost::bind + ( + &openssl_operation::async_read_handler, + this, + boost::asio::placeholders::error, + boost::asio::placeholders::bytes_transferred + ) + ); + } + + void async_read_handler(const boost::asio::error& error, size_t bytes_recvd) + { + if (!error) + { + recv_buf_.data_added(bytes_recvd); + + // Pass the received data to SSL + int written = ::BIO_write + ( + ssl_bio_, + recv_buf_.get_data_start(), + recv_buf_.get_data_len() + ); + + if (written > 0) + { + recv_buf_.data_removed(written); + } else if (written < 0) + // Some serios error with BIO.... + handler_(boost::asio::error(boost::asio::error::no_recovery), 0); + + // and try the SSL primitive again + start(); + } + else + { + // Error in network level... + // SSL can't continue either... + handler_(error, 0); + } + } + + // Syncronous functions... + int do_sync_write(bool is_operation_done, int rc) + { + int len = ::BIO_ctrl_pending( ssl_bio_ ); + if ( len ) + { + // There is something to write into net, do it... + len = (int)send_buf_.get_unused_len() > len? + len: + send_buf_.get_unused_len(); + + // Read outgoing data from bio + len = ::BIO_read( ssl_bio_, send_buf_.get_unused_start(), len); + + if (len > 0) + { + size_t sent_len = boost::asio::write( + socket_, + boost::asio::buffer(send_buf_.get_unused_start(), len) + ); + + send_buf_.data_added(len); + send_buf_.data_removed(sent_len); + } + else + // Seems like fatal error + // reading from SSL BIO has failed... + throw boost::asio::error(boost::asio::error::no_recovery); + } + + if (is_operation_done) + // Finish the operation, with success + return rc; + + // Operation is not finished, read data from net... + return do_sync_read(); + } + + int do_sync_read() + { + size_t len = socket_.boost::asio::read_some + ( + boost::asio::buffer(recv_buf_.get_unused_start(), + recv_buf_.get_unused_len()) + ); + + // Write data to ssl + recv_buf_.data_added(len); + + // Pass the received data to SSL + int written = ::BIO_write + ( + ssl_bio_, + recv_buf_.get_data_start(), + recv_buf_.get_data_len() + ); + + if (written > 0) + { + recv_buf_.data_removed(written); + } else if (written < 0) + // Some serios error with BIO.... + throw boost::asio::error(boost::asio::error::no_recovery); + + // Try the operation again + return start(); + } +}; // class openssl_operation + +} // namespace detail +} // namespace ssl +} // namespace asio +} // namespace boost + +#include + +#endif // BOOST_ASIO_SSL_DETAIL_OPENSSL_OPERATION_HPP diff --git a/include/boost/asio/ssl/detail/openssl_stream_service.hpp b/include/boost/asio/ssl/detail/openssl_stream_service.hpp new file mode 100644 index 00000000..56ac7541 --- /dev/null +++ b/include/boost/asio/ssl/detail/openssl_stream_service.hpp @@ -0,0 +1,426 @@ +// +// stream_service.hpp +// ~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2005 Voipster / Indrek dot Juhani at voipster dot com +// Copyright (c) 2005 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_SSL_DETAIL_OPENSSL_STREAM_SERVICE_HPP +#define BOOST_ASIO_SSL_DETAIL_OPENSSL_STREAM_SERVICE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace boost { +namespace asio { +namespace ssl { +namespace detail { + +template +class openssl_stream_service + : private boost::noncopyable +{ +public: + // The demuxer type. + typedef basic_demuxer > demuxer_type; + +private: + //Base handler for asyncrhonous operations + template + class base_handler + { + public: + typedef boost::function func_t; + + base_handler(demuxer_type& d) + : op_(NULL) + , demuxer_(d) + , work_(d) + {} + + void do_func(const boost::asio::error& error, size_t size) + { + func_(error, size); + } + + void set_operation(openssl_operation* op) { op_ = op; } + void set_func(func_t func) { func_ = func; } + + ~base_handler() + { + delete op_; + } + + private: + func_t func_; + openssl_operation* op_; + demuxer_type& demuxer_; + typename demuxer_type::work work_; + }; // class base_handler + + // Handler for asynchronous IO (write/read) operations + template + class io_handler + : public base_handler + { + public: + io_handler(Handler handler, demuxer_type& d) + : base_handler(d) + , handler_(handler) + { + set_func(boost::bind( + &io_handler::handler_impl, + this, boost::arg<1>(), boost::arg<2>() )); + } + + private: + Handler handler_; + void handler_impl(const boost::asio::error& error, size_t size) + { + handler_(error, size); + delete this; + } + }; // class io_handler + + // Handler for asyncrhonous handshake (connect, accept) functions + template + class handshake_handler + : public base_handler + { + public: + handshake_handler(Handler handler, demuxer_type& d) + : base_handler(d) + , handler_(handler) + { + set_func(boost::bind( + &handshake_handler::handler_impl, + this, boost::arg<1>(), boost::arg<2>() )); + } + + private: + Handler handler_; + void handler_impl(const boost::asio::error& error, size_t) + { + handler_(error); + delete this; + } + + }; // class handshake_handler + + // Handler for asyncrhonous shutdown + template + class shutdown_handler + : public base_handler + { + public: + shutdown_handler(Handler handler, demuxer_type& d) + : base_handler(d), + handler_(handler) + { + set_func(boost::bind( + &shutdown_handler::handler_impl, + this, boost::arg<1>(), boost::arg<2>() )); + } + + private: + Handler handler_; + void handler_impl(const boost::asio::error& error, size_t) + { + handler_(error); + delete this; + } + }; // class shutdown_handler + +public: + // The implementation type. + typedef struct impl_struct + { + ::SSL* ssl; + ::BIO* ext_bio; + } * impl_type; + + // Construct a new stream socket service for the specified demuxer. + explicit openssl_stream_service(demuxer_type& demuxer) + : demuxer_(demuxer) + { + } + + // Get the demuxer associated with the service. + demuxer_type& demuxer() + { + return demuxer_; + } + + // Return a null stream implementation. + impl_type null() const + { + return 0; + } + + // Create a new stream implementation. + template + void create(impl_type& impl, Stream& next_layer, + basic_context& context) + { + impl = new impl_struct; + impl->ssl = ::SSL_new(context.impl()); + ::SSL_set_mode(impl->ssl, SSL_MODE_ENABLE_PARTIAL_WRITE); + ::BIO* int_bio = 0; + impl->ext_bio = 0; + ::BIO_new_bio_pair(&int_bio, 8192, &impl->ext_bio, 8192); + ::SSL_set_bio(impl->ssl, int_bio, int_bio); + } + + // Destroy a stream implementation. + template + void destroy(impl_type& impl, Stream& next_layer) + { + if (impl != 0) + { + ::BIO_free(impl->ext_bio); + ::SSL_free(impl->ssl); + delete impl; + impl = 0; + } + } + + // Perform SSL handshaking. + template + void handshake(impl_type& impl, Stream& next_layer, + stream_base::handshake_type type, Error_Handler error_handler) + { + openssl_operation op( + type == stream_base::client ? + &::SSL_connect: + &::SSL_accept, + next_layer, + impl->ssl, + impl->ext_bio); + op.start(); + } + + // Start an asynchronous SSL handshake. + template + void async_handshake(impl_type& impl, Stream& next_layer, + stream_base::handshake_type type, Handler handler) + { + typedef handshake_handler connect_handler; + + connect_handler* local_handler = + new connect_handler(handler, demuxer_); + + openssl_operation* op = new openssl_operation + ( + type == stream_base::client ? + &::SSL_connect: + &::SSL_accept, + next_layer, + impl->ssl, + impl->ext_bio, + boost::bind + ( + &base_handler::do_func, + local_handler, + boost::arg<1>(), + boost::arg<2>() + ) + ); + local_handler->set_operation(op); + + demuxer_.post(boost::bind(&openssl_operation::start, op)); + } + + // Shut down SSL on the stream. + template + void shutdown(impl_type& impl, Stream& next_layer, + Error_Handler error_handler) + { + openssl_operation op( + &::SSL_shutdown, + next_layer, + impl->ssl, + impl->ext_bio); + op.start(); + } + + // Asynchronously shut down SSL on the stream. + template + void async_shutdown(impl_type& impl, Stream& next_layer, Handler handler) + { + typedef shutdown_handler disconnect_handler; + + disconnect_handler* local_handler = + new disconnect_handler(handler, demuxer_); + + openssl_operation* op = new openssl_operation + ( + &::SSL_shutdown, + next_layer, + impl->ssl, + impl->ext_bio, + boost::bind + ( + &base_handler::do_func, + local_handler, + boost::arg<1>(), + boost::arg<2>() + ) + ); + local_handler->set_operation(op); + + demuxer_.post(boost::bind(&openssl_operation::start, op)); + } + + // Write some data to the stream. + template + std::size_t write_some(impl_type& impl, Stream& next_layer, + const Const_Buffers& buffers, Error_Handler error_handler) + { + boost::function send_func = + boost::bind(&::SSL_write, boost::arg<1>(), + boost::asio::buffer_cast(*buffers.begin()), + static_cast(boost::asio::buffer_size(*buffers.begin()))); + openssl_operation op( + send_func, + next_layer, + impl->ssl, + impl->ext_bio + ); + return static_cast(op.start()); + } + + // Start an asynchronous write. + template + void async_write_some(impl_type& impl, Stream& next_layer, + const Const_Buffers& buffers, Handler handler) + { + typedef io_handler send_handler; + + send_handler* local_handler = new send_handler(handler, demuxer_); + + boost::function send_func = + boost::bind(&::SSL_write, boost::arg<1>(), + boost::asio::buffer_cast(*buffers.begin()), + static_cast(boost::asio::buffer_size(*buffers.begin()))); + + openssl_operation* op = new openssl_operation + ( + send_func, + next_layer, + impl->ssl, + impl->ext_bio, + boost::bind + ( + &base_handler::do_func, + local_handler, + boost::arg<1>(), + boost::arg<2>() + ) + ); + local_handler->set_operation(op); + + demuxer_.post(boost::bind(&openssl_operation::start, op)); + } + + // Read some data from the stream. + template + std::size_t read_some(impl_type& impl, Stream& next_layer, + const Mutable_Buffers& buffers, Error_Handler error_handler) + { + boost::function recv_func = + boost::bind(&::SSL_read, boost::arg<1>(), + boost::asio::buffer_cast(*buffers.begin()), + boost::asio::buffer_size(*buffers.begin())); + openssl_operation op(recv_func, + next_layer, + impl->ssl, + impl->ext_bio + ); + + return static_cast(op.start()); + } + + // Start an asynchronous read. + template + void async_read_some(impl_type& impl, Stream& next_layer, + const Mutable_Buffers& buffers, Handler handler) + { + typedef io_handler recv_handler; + + recv_handler* local_handler = new recv_handler(handler, demuxer_); + + boost::function recv_func = + boost::bind(&::SSL_read, boost::arg<1>(), + boost::asio::buffer_cast(*buffers.begin()), + boost::asio::buffer_size(*buffers.begin())); + + openssl_operation* op = new openssl_operation + ( + recv_func, + next_layer, + impl->ssl, + impl->ext_bio, + boost::bind + ( + &base_handler::do_func, + local_handler, + boost::arg<1>(), + boost::arg<2>() + ) + ); + local_handler->set_operation(op); + + demuxer_.post(boost::bind(&openssl_operation::start, op)); + } + + // Peek at the incoming data on the stream. + template + std::size_t peek(impl_type& impl, Stream& next_layer, + const Mutable_Buffers& buffers, Error_Handler error_handler) + { + return 0; + } + + // Determine the amount of data that may be read without blocking. + template + std::size_t in_avail(impl_type& impl, Stream& next_layer, + Error_Handler error_handler) + { + return 0; + } + +private: + // The demuxer used to dispatch handlers. + demuxer_type& demuxer_; +}; + +} // namespace detail +} // namespace ssl +} // namespace asio +} // namespace boost + +#include + +#endif // BOOST_ASIO_SSL_DETAIL_OPENSSL_STREAM_SERVICE_HPP diff --git a/include/boost/asio/ssl/detail/openssl_types.hpp b/include/boost/asio/ssl/detail/openssl_types.hpp new file mode 100644 index 00000000..c96ee2ef --- /dev/null +++ b/include/boost/asio/ssl/detail/openssl_types.hpp @@ -0,0 +1,26 @@ +// +// openssl_types.hpp +// ~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2005 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_SSL_DETAIL_OPENSSL_TYPES_HPP +#define BOOST_ASIO_SSL_DETAIL_OPENSSL_TYPES_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include + +#include +#include +#include + +#include + +#endif // BOOST_ASIO_SSL_DETAIL_OPENSSL_TYPES_HPP diff --git a/include/boost/asio/ssl/stream.hpp b/include/boost/asio/ssl/stream.hpp new file mode 100644 index 00000000..7dcd3bb8 --- /dev/null +++ b/include/boost/asio/ssl/stream.hpp @@ -0,0 +1,515 @@ +// +// stream.hpp +// ~~~~~~~~~~ +// +// Copyright (c) 2005 Voipster / Indrek dot Juhani at voipster dot com +// Copyright (c) 2005 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_SSL_STREAM_HPP +#define BOOST_ASIO_SSL_STREAM_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace boost { +namespace asio { +namespace ssl { + +/// Provides stream-oriented functionality using SSL. +/** + * The stream class template provides asynchronous and blocking stream-oriented + * functionality using SSL. + * + * @par Thread Safety: + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Unsafe. + * + * @par Example: + * To use the SSL stream template with a stream_socket, you would write: + * @code + * boost::asio::demuxer d; + * boost::asio::ssl::context context(d, boost::asio::ssl::context::sslv23); + * boost::asio::ssl::stream sock(demuxer, context); + * @endcode + * + * @par Concepts: + * Async_Object, Async_Read_Stream, Async_Write_Stream, Error_Source, Stream, + * Sync_Read_Stream, Sync_Write_Stream. + */ +template > +class stream + : public stream_base, + private boost::noncopyable +{ +public: + /// The type of the next layer. + typedef typename boost::remove_reference::type next_layer_type; + + /// The type of the lowest layer. + typedef typename next_layer_type::lowest_layer_type lowest_layer_type; + + /// The demuxer type for this asynchronous type. + typedef typename next_layer_type::demuxer_type demuxer_type; + + /// The type used for reporting errors. + typedef typename next_layer_type::error_type error_type; + + /// The type of the service that will be used to provide stream operations. + typedef Service service_type; + + /// The native implementation type of the stream. + typedef typename service_type::impl_type impl_type; + + /// Construct a stream. + /** + * This constructor creates a stream and initialises the underlying stream + * object. + * + * @param arg The argument to be passed to initialise the underlying stream. + * + * @param context The SSL context to be used for the stream. + */ + template + explicit stream(Arg& arg, basic_context& context) + : next_layer_(arg), + service_(next_layer_.demuxer().get_service(service_factory())), + impl_(service_.null()) + { + service_.create(impl_, next_layer_, context); + } + + /// Destructor. + ~stream() + { + service_.destroy(impl_, next_layer_); + } + + /// Get the demuxer associated with the asynchronous object. + /** + * This function may be used to obtain the demuxer object that the stream uses + * to dispatch handlers for asynchronous operations. + * + * @return A reference to the demuxer object that stream will use to dispatch + * handlers. Ownership is not transferred to the caller. + */ + demuxer_type& demuxer() + { + return next_layer_.demuxer(); + } + + /// Get a reference to the next layer. + /** + * This function returns a reference to the next layer in a stack of stream + * layers. + * + * @return A reference to the next layer in the stack of stream layers. + * Ownership is not transferred to the caller. + */ + next_layer_type& next_layer() + { + return next_layer_; + } + + /// Get a reference to the lowest layer. + /** + * This function returns a reference to the lowest layer in a stack of + * stream layers. + * + * @return A reference to the lowest layer in the stack of stream layers. + * Ownership is not transferred to the caller. + */ + lowest_layer_type& lowest_layer() + { + return next_layer_.lowest_layer(); + } + + /// Get the underlying implementation in the native type. + /** + * This function may be used to obtain the underlying implementation of the + * context. This is intended to allow access to stream functionality that is + * not otherwise provided. + */ + impl_type impl() + { + return impl_; + } + + /// Perform SSL handshaking. + /** + * This function is used to perform SSL handshaking on the stream. The + * function call will block until handshaking is complete or an error occurs. + * + * @param type The type of handshaking to be performed, i.e. as a client or as + * a server. + * + * @throws boost::asio::error Thrown on failure. + */ + void handshake(handshake_type type) + { + service_.handshake(impl_, next_layer_, type, throw_error()); + } + + /// Perform SSL handshaking. + /** + * This function is used to perform SSL handshaking on the stream. The + * function call will block until handshaking is complete or an error occurs. + * + * @param type The type of handshaking to be performed, i.e. as a client or as + * a server. + * + * @param error_handler The handler to be called when an error occurs. Copies + * will be made of the handler as required. The equivalent function signature + * of the handler must be: + * @code void error_handler( + * const boost::asio::error& error // Result of operation + * ); @endcode + */ + template + void handshake(handshake_type type, Error_Handler error_handler) + { + service_.handshake(impl_, next_layer_, type, error_handler); + } + + /// Start an asynchronous SSL handshake. + /** + * This function is used to asynchronously perform an SSL handshake on the + * stream. This function call always returns immediately. + * + * @param type The type of handshaking to be performed, i.e. as a client or as + * a server. + * + * @param handler The handler to be called when the handshake operation + * completes. Copies will be made of the handler as required. The equivalent + * function signature of the handler must be: + * @code void handler( + * const boost::asio::error& error, // Result of operation + * ); @endcode + */ + template + void async_handshake(handshake_type type, Handler handler) + { + service_.async_handshake(impl_, next_layer_, type, handler); + } + + /// Shut down SSL on the stream. + /** + * This function is used to shut down SSL on the stream. The function call + * will block until SSL has been shut down or an error occurs. + * + * @throws boost::asio::error Thrown on failure. + */ + void shutdown() + { + service_.shutdown(impl_, next_layer_, throw_error()); + } + + /// Shut down SSL on the stream. + /** + * This function is used to shut down SSL on the stream. The function call + * will block until SSL has been shut down or an error occurs. + * + * @param error_handler The handler to be called when an error occurs. Copies + * will be made of the handler as required. The equivalent function signature + * of the handler must be: + * @code void error_handler( + * const boost::asio::error& error // Result of operation + * ); @endcode + */ + template + void shutdown(Error_Handler error_handler) + { + service_.shutdown(impl_, next_layer_, error_handler); + } + + /// Asynchronously shut down SSL on the stream. + /** + * This function is used to asynchronously shut down SSL on the stream. This + * function call always returns immediately. + * + * @param handler The handler to be called when the handshake operation + * completes. Copies will be made of the handler as required. The equivalent + * function signature of the handler must be: + * @code void handler( + * const boost::asio::error& error, // Result of operation + * ); @endcode + */ + template + void async_shutdown(Handler handler) + { + service_.async_shutdown(impl_, next_layer_, handler); + } + + /// Write some data to the stream. + /** + * This function is used to write data on the stream. The function call will + * block until one or more bytes of data has been written successfully, or + * until an error occurs. + * + * @param buffers The data to be written. + * + * @returns The number of bytes written. + * + * @throws boost::asio::error Thrown on failure. + * + * @note The write_some operation may not transmit all of the data to the + * peer. Consider using the @ref write function if you need to ensure that all + * data is written before the blocking operation completes. + */ + template + std::size_t write_some(const Const_Buffers& buffers) + { + return service_.write_some(impl_, next_layer_, buffers, throw_error()); + } + + /// Write some data to the stream. + /** + * This function is used to write data on the stream. The function call will + * block until one or more bytes of data has been written successfully, or + * until an error occurs. + * + * @param buffers The data to be written to the stream. + * + * @param error_handler The handler to be called when an error occurs. Copies + * will be made of the handler as required. The equivalent function signature + * of the handler must be: + * @code void error_handler( + * const boost::asio::error& error // Result of operation. + * ); @endcode + * + * @returns The number of bytes written. Returns 0 if an error occurred and + * the error handler did not throw an exception. + * + * @note The write_some operation may not transmit all of the data to the + * peer. Consider using the @ref write function if you need to ensure that all + * data is written before the blocking operation completes. + */ + template + std::size_t write_some(const Const_Buffers& buffers, + Error_Handler error_handler) + { + return service_.write_some(impl_, next_layer_, buffers, error_handler); + } + + /// Start an asynchronous write. + /** + * This function is used to asynchronously write one or more bytes of data to + * the stream. The function call always returns immediately. + * + * @param buffers The data to be written to the stream. Although the buffers + * object may be copied as necessary, ownership of the underlying buffers is + * retained by the caller, which must guarantee that they remain valid until + * the handler is called. + * + * @param handler The handler to be called when the write operation completes. + * Copies will be made of the handler as required. The equivalent function + * signature of the handler must be: + * @code void handler( + * const boost::asio::error& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes written. + * ); @endcode + * + * @note The async_write_some operation may not transmit all of the data to + * the peer. Consider using the @ref async_write function if you need to + * ensure that all data is written before the blocking operation completes. + */ + template + void async_write_some(const Const_Buffers& buffers, Handler handler) + { + service_.async_write_some(impl_, next_layer_, buffers, handler); + } + + /// Read some data from the stream. + /** + * This function is used to read data from the stream. The function call will + * block until one or more bytes of data has been read successfully, or until + * an error occurs. + * + * @param buffers The buffers into which the data will be read. + * + * @returns The number of bytes read. + * + * @throws boost::asio::error Thrown on failure. + * + * @note The read_some operation may not read all of the requested number of + * bytes. Consider using the @ref read function if you need to ensure that the + * requested amount of data is read before the blocking operation completes. + */ + template + std::size_t read_some(const Mutable_Buffers& buffers) + { + return service_.read_some(impl_, next_layer_, buffers, throw_error()); + } + + /// Read some data from the stream. + /** + * This function is used to read data from the stream. The function call will + * block until one or more bytes of data has been read successfully, or until + * an error occurs. + * + * @param buffers The buffers into which the data will be read. + * + * @param error_handler The handler to be called when an error occurs. Copies + * will be made of the handler as required. The equivalent function signature + * of the handler must be: + * @code void error_handler( + * const boost::asio::error& error // Result of operation. + * ); @endcode + * + * @returns The number of bytes read. Returns 0 if an error occurred and the + * error handler did not throw an exception. + * + * @note The read_some operation may not read all of the requested number of + * bytes. Consider using the @ref read function if you need to ensure that the + * requested amount of data is read before the blocking operation completes. + */ + template + std::size_t read_some(const Mutable_Buffers& buffers, + Error_Handler error_handler) + { + return service_.read_some(impl_, next_layer_, buffers, error_handler); + } + + /// Start an asynchronous read. + /** + * This function is used to asynchronously read one or more bytes of data from + * the stream. The function call always returns immediately. + * + * @param buffers The buffers into which the data will be read. Although the + * buffers object may be copied as necessary, ownership of the underlying + * buffers is retained by the caller, which must guarantee that they remain + * valid until the handler is called. + * + * @param handler The handler to be called when the read operation completes. + * Copies will be made of the handler as required. The equivalent function + * signature of the handler must be: + * @code void handler( + * const boost::asio::error& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes read. + * ); @endcode + * + * @note The async_read_some operation may not read all of the requested + * number of bytes. Consider using the @ref async_read function if you need to + * ensure that the requested amount of data is read before the asynchronous + * operation completes. + */ + template + void async_read_some(const Mutable_Buffers& buffers, Handler handler) + { + service_.async_read_some(impl_, next_layer_, buffers, handler); + } + + /// Peek at the incoming data on the stream. + /** + * This function is used to peek at the incoming data on the stream, without + * removing it from the input queue. The function call will block until data + * has been read successfully or an error occurs. + * + * @param buffers The buffers into which the data will be read. + * + * @returns The number of bytes read. + * + * @throws boost::asio::error Thrown on failure. + */ + template + std::size_t peek(const Mutable_Buffers& buffers) + { + return service_.peek(impl_, next_layer_, buffers, throw_error()); + } + + /// Peek at the incoming data on the stream. + /** + * This function is used to peek at the incoming data on the stream, withoutxi + * removing it from the input queue. The function call will block until data + * has been read successfully or an error occurs. + * + * @param buffers The buffers into which the data will be read. + * + * @param error_handler The handler to be called when an error occurs. Copies + * will be made of the handler as required. The equivalent function signature + * of the handler must be: + * @code void error_handler( + * const boost::asio::error& error // Result of operation. + * ); @endcode + * + * @returns The number of bytes read. Returns 0 if an error occurred and the + * error handler did not throw an exception. + */ + template + std::size_t peek(const Mutable_Buffers& buffers, Error_Handler error_handler) + { + return service_.peek(impl_, next_layer_, buffers, error_handler); + } + + /// Determine the amount of data that may be read without blocking. + /** + * This function is used to determine the amount of data, in bytes, that may + * be read from the stream without blocking. + * + * @returns The number of bytes of data that can be read without blocking. + * + * @throws boost::asio::error Thrown on failure. + */ + std::size_t in_avail() + { + return service_.in_avail(impl_, next_layer_, throw_error()); + } + + /// Determine the amount of data that may be read without blocking. + /** + * This function is used to determine the amount of data, in bytes, that may + * be read from the stream without blocking. + * + * @param error_handler The handler to be called when an error occurs. Copies + * will be made of the handler as required. The equivalent function signature + * of the handler must be: + * @code void error_handler( + * const boost::asio::error& error // Result of operation + * ); @endcode + * + * @returns The number of bytes of data that can be read without blocking. + */ + template + std::size_t in_avail(Error_Handler error_handler) + { + return service_.in_avail(impl_, next_layer_, error_handler); + } + +private: + /// The next layer. + Stream next_layer_; + + /// The backend service implementation. + service_type& service_; + + /// The underlying native implementation. + impl_type impl_; +}; + +} // namespace ssl +} // namespace asio +} // namespace boost + +#include + +#endif // BOOST_ASIO_SSL_STREAM_HPP diff --git a/include/boost/asio/ssl/stream_base.hpp b/include/boost/asio/ssl/stream_base.hpp new file mode 100644 index 00000000..b1b79e2f --- /dev/null +++ b/include/boost/asio/ssl/stream_base.hpp @@ -0,0 +1,62 @@ +// +// stream_base.hpp +// ~~~~~~~~~~~~~~~ +// +// Copyright (c) 2005 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_SSL_STREAM_BASE_HPP +#define BOOST_ASIO_SSL_STREAM_BASE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include + +#include +#include +#include + +namespace boost { +namespace asio { +namespace ssl { + +/// The stream_base class is used as a base for the boost::asio::ssl::stream +/// class template so that we have a common place to define various enums. +class stream_base +{ +public: + /// Different handshake types. + enum handshake_type + { + /// Perform handshaking as a client. + client, + + /// Perform handshaking as a server. + server + }; + +protected: + /// Protected destructor to prevent deletion through this type. + ~stream_base() + { + } + +#if BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564)) +private: + // Workaround to enable the empty base optimisation with Borland C++. + char dummy_; +#endif +}; + +} // namespace ssl +} // namespace asio +} // namespace boost + +#include + +#endif // BOOST_ASIO_SSL_STREAM_BASE_HPP diff --git a/include/boost/asio/ssl/stream_service.hpp b/include/boost/asio/ssl/stream_service.hpp new file mode 100644 index 00000000..64440739 --- /dev/null +++ b/include/boost/asio/ssl/stream_service.hpp @@ -0,0 +1,182 @@ +// +// stream_service.hpp +// ~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2005 Voipster / Indrek dot Juhani at voipster dot com +// Copyright (c) 2005 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_SSL_STREAM_SERVICE_HPP +#define BOOST_ASIO_SSL_STREAM_SERVICE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +namespace boost { +namespace asio { +namespace ssl { + +/// Default service implementation for an SSL stream. +template > +class stream_service + : private boost::noncopyable +{ +public: + /// The demuxer type. + typedef basic_demuxer > demuxer_type; + +private: + // The type of the platform-specific implementation. + typedef detail::openssl_stream_service service_impl_type; + +public: + /// The type of a stream implementation. +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined impl_type; +#else + typedef typename service_impl_type::impl_type impl_type; +#endif + + /// Construct a new stream service for the specified demuxer. + explicit stream_service(demuxer_type& demuxer) + : service_impl_(demuxer.get_service(service_factory())) + { + } + + /// Get the demuxer associated with the service. + demuxer_type& demuxer() + { + return service_impl_.demuxer(); + } + + /// Return a null stream implementation. + impl_type null() const + { + return service_impl_.null(); + } + + /// Create a new stream implementation. + template + void create(impl_type& impl, Stream& next_layer, + basic_context& context) + { + service_impl_.create(impl, next_layer, context); + } + + /// Destroy a stream implementation. + template + void destroy(impl_type& impl, Stream& next_layer) + { + service_impl_.destroy(impl, next_layer); + } + + /// Perform SSL handshaking. + template + void handshake(impl_type& impl, Stream& next_layer, + stream_base::handshake_type type, Error_Handler error_handler) + { + service_impl_.handshake(impl, next_layer, type, error_handler); + } + + /// Start an asynchronous SSL handshake. + template + void async_handshake(impl_type& impl, Stream& next_layer, + stream_base::handshake_type type, Handler handler) + { + service_impl_.async_handshake(impl, next_layer, type, handler); + } + + /// Shut down SSL on the stream. + template + void shutdown(impl_type& impl, Stream& next_layer, + Error_Handler error_handler) + { + service_impl_.shutdown(impl, next_layer, error_handler); + } + + /// Asynchronously shut down SSL on the stream. + template + void async_shutdown(impl_type& impl, Stream& next_layer, Handler handler) + { + service_impl_.async_shutdown(impl, next_layer, handler); + } + + /// Write some data to the stream. + template + std::size_t write_some(impl_type& impl, Stream& next_layer, + const Const_Buffers& buffers, Error_Handler error_handler) + { + return service_impl_.write_some(impl, next_layer, buffers, error_handler); + } + + /// Start an asynchronous write. + template + void async_write_some(impl_type& impl, Stream& next_layer, + const Const_Buffers& buffers, Handler handler) + { + service_impl_.async_write_some(impl, next_layer, buffers, handler); + } + + /// Read some data from the stream. + template + std::size_t read_some(impl_type& impl, Stream& next_layer, + const Mutable_Buffers& buffers, Error_Handler error_handler) + { + return service_impl_.read_some(impl, next_layer, buffers, error_handler); + } + + /// Start an asynchronous read. + template + void async_read_some(impl_type& impl, Stream& next_layer, + const Mutable_Buffers& buffers, Handler handler) + { + service_impl_.async_read_some(impl, next_layer, buffers, handler); + } + + /// Peek at the incoming data on the stream. + template + std::size_t peek(impl_type& impl, Stream& next_layer, + const Mutable_Buffers& buffers, Error_Handler error_handler) + { + return service_impl_.peek(impl, next_layer, buffers, error_handler); + } + + /// Determine the amount of data that may be read without blocking. + template + std::size_t in_avail(impl_type& impl, Stream& next_layer, + Error_Handler error_handler) + { + return service_impl_.in_avail(impl, next_layer, error_handler); + } + +private: + // The service that provides the platform-specific implementation. + service_impl_type& service_impl_; +}; + +} // namespace ssl +} // namespace asio +} // namespace boost + +#include + +#endif // BOOST_ASIO_SSL_STREAM_SERVICE_HPP diff --git a/include/boost/asio/stream_socket.hpp b/include/boost/asio/stream_socket.hpp new file mode 100644 index 00000000..d8825ec0 --- /dev/null +++ b/include/boost/asio/stream_socket.hpp @@ -0,0 +1,34 @@ +// +// stream_socket.hpp +// ~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2005 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_STREAM_SOCKET_HPP +#define BOOST_ASIO_STREAM_SOCKET_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include + +#include +#include + +namespace boost { +namespace asio { + +/// Typedef for the typical usage of stream_socket. +typedef basic_stream_socket > stream_socket; + +} // namespace asio +} // namespace boost + +#include + +#endif // BOOST_ASIO_STREAM_SOCKET_HPP diff --git a/include/boost/asio/stream_socket_service.hpp b/include/boost/asio/stream_socket_service.hpp new file mode 100644 index 00000000..3637caeb --- /dev/null +++ b/include/boost/asio/stream_socket_service.hpp @@ -0,0 +1,226 @@ +// +// stream_socket_service.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2005 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_STREAM_SOCKET_SERVICE_HPP +#define BOOST_ASIO_STREAM_SOCKET_SERVICE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace boost { +namespace asio { + +/// Default service implementation for a stream socket. +template > +class stream_socket_service + : private noncopyable +{ +public: + /// The demuxer type. + typedef basic_demuxer > demuxer_type; + +private: + // The type of the platform-specific implementation. +#if defined(BOOST_ASIO_HAS_IOCP_DEMUXER) + typedef detail::win_iocp_socket_service service_impl_type; +#elif defined(BOOST_ASIO_HAS_EPOLL_REACTOR) + typedef detail::reactive_socket_service< + demuxer_type, detail::epoll_reactor > service_impl_type; +#elif defined(BOOST_ASIO_HAS_KQUEUE_REACTOR) + typedef detail::reactive_socket_service< + demuxer_type, detail::kqueue_reactor > service_impl_type; +#else + typedef detail::reactive_socket_service< + demuxer_type, detail::select_reactor > service_impl_type; +#endif + +public: + /// The type of a stream socket. +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined impl_type; +#else + typedef typename service_impl_type::impl_type impl_type; +#endif + + /// Construct a new stream socket service for the specified demuxer. + explicit stream_socket_service(demuxer_type& demuxer) + : service_impl_(demuxer.get_service(service_factory())) + { + } + + /// Get the demuxer associated with the service. + demuxer_type& demuxer() + { + return service_impl_.demuxer(); + } + + /// Return a null stream socket implementation. + impl_type null() const + { + return service_impl_.null(); + } + + /// Open a new stream socket implementation. + template + void open(impl_type& impl, const Protocol& protocol, + Error_Handler error_handler) + { + if (protocol.type() == SOCK_STREAM) + service_impl_.open(impl, protocol, error_handler); + else + error_handler(boost::asio::error(boost::asio::error::invalid_argument)); + } + + /// Assign a new stream socket implementation. + void assign(impl_type& impl, impl_type new_impl) + { + service_impl_.assign(impl, new_impl); + } + + /// Close a stream socket implementation. + template + void close(impl_type& impl, Error_Handler error_handler) + { + service_impl_.close(impl, error_handler); + } + + /// Bind the stream socket to the specified local endpoint. + template + void bind(impl_type& impl, const Endpoint& endpoint, + Error_Handler error_handler) + { + service_impl_.bind(impl, endpoint, error_handler); + } + + /// Connect the stream socket to the specified endpoint. + template + void connect(impl_type& impl, const Endpoint& peer_endpoint, + Error_Handler error_handler) + { + service_impl_.connect(impl, peer_endpoint, error_handler); + } + + /// Start an asynchronous connect. + template + void async_connect(impl_type& impl, const Endpoint& peer_endpoint, + Handler handler) + { + service_impl_.async_connect(impl, peer_endpoint, handler); + } + + /// Set a socket option. + template + void set_option(impl_type& impl, const Option& option, + Error_Handler error_handler) + { + service_impl_.set_option(impl, option, error_handler); + } + + /// Get a socket option. + template + void get_option(const impl_type& impl, Option& option, + Error_Handler error_handler) const + { + service_impl_.get_option(impl, option, error_handler); + } + + /// Perform an IO control command on the socket. + template + void io_control(impl_type& impl, IO_Control_Command& command, + Error_Handler error_handler) + { + service_impl_.io_control(impl, command, error_handler); + } + + /// Get the local endpoint. + template + void get_local_endpoint(const impl_type& impl, Endpoint& endpoint, + Error_Handler error_handler) const + { + service_impl_.get_local_endpoint(impl, endpoint, error_handler); + } + + /// Get the remote endpoint. + template + void get_remote_endpoint(const impl_type& impl, Endpoint& endpoint, + Error_Handler error_handler) const + { + service_impl_.get_remote_endpoint(impl, endpoint, error_handler); + } + + /// Disable sends or receives on the socket. + template + void shutdown(impl_type& impl, socket_base::shutdown_type what, + Error_Handler error_handler) + { + service_impl_.shutdown(impl, what, error_handler); + } + + /// Send the given data to the peer. + template + std::size_t send(impl_type& impl, const Const_Buffers& buffers, + socket_base::message_flags flags, Error_Handler error_handler) + { + return service_impl_.send(impl, buffers, flags, error_handler); + } + + /// Start an asynchronous send. + template + void async_send(impl_type& impl, const Const_Buffers& buffers, + socket_base::message_flags flags, Handler handler) + { + service_impl_.async_send(impl, buffers, flags, handler); + } + + /// Receive some data from the peer. + template + std::size_t receive(impl_type& impl, const Mutable_Buffers& buffers, + socket_base::message_flags flags, Error_Handler error_handler) + { + return service_impl_.receive(impl, buffers, flags, error_handler); + } + + /// Start an asynchronous receive. + template + void async_receive(impl_type& impl, const Mutable_Buffers& buffers, + socket_base::message_flags flags, Handler handler) + { + service_impl_.async_receive(impl, buffers, flags, handler); + } + +private: + // The service that provides the platform-specific implementation. + service_impl_type& service_impl_; +}; + +} // namespace asio +} // namespace boost + +#include + +#endif // BOOST_ASIO_STREAM_SOCKET_SERVICE_HPP diff --git a/include/boost/asio/system_exception.hpp b/include/boost/asio/system_exception.hpp new file mode 100644 index 00000000..feb2411a --- /dev/null +++ b/include/boost/asio/system_exception.hpp @@ -0,0 +1,198 @@ +// +// error.hpp +// ~~~~~~~~~ +// +// Copyright (c) 2003-2005 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_SYSTEM_EXCEPTION_HPP +#define BOOST_ASIO_SYSTEM_EXCEPTION_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#if BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564)) +# include +#endif // BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564)) +#include + +#include + +namespace boost { +namespace asio { + +/// The system_exception class is used to represent system conditions that +/// prevent the library from operating correctly. +class system_exception + : public std::exception +{ +public: + /// Construct with a specific context and error code. + system_exception(const std::string& context, int code) + : context_(context), + code_(code) + { + } + + /// Copy constructor. + system_exception(const system_exception& e) + : context_(e.context_), + code_(e.code_) + { + } + + /// Destructor. + virtual ~system_exception() throw () + { + } + + /// Assignment operator. + system_exception& operator=(const system_exception& e) + { + context_ = e.context_; + code_ = e.code_; + what_.reset(); + return *this; + } + + /// Get a string representation of the exception. + virtual const char* what() const throw () + { +#if defined(BOOST_WINDOWS) + try + { + if (!what_) + { + char* msg = 0; + DWORD length = ::FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER + | FORMAT_MESSAGE_FROM_SYSTEM + | FORMAT_MESSAGE_IGNORE_INSERTS, 0, code_, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (char*)&msg, 0, 0); + detail::win_local_free_on_block_exit local_free_obj(msg); + if (length && msg[length - 1] == '\n') + msg[--length] = '\0'; + if (length && msg[length - 1] == '\r') + msg[--length] = '\0'; + if (length) + { + std::string tmp(context_); + tmp += ": "; + tmp += msg; + what_.reset(new std::string(tmp)); + } + else + { + return "asio system_exception"; + } + } + return what_->c_str(); + } + catch (std::exception&) + { + return "asio system_exception"; + } +#elif defined(__sun) + return strerror(code_); +#elif defined(__MACH__) && defined(__APPLE__) + try + { + char buf[256] = ""; + strerror_r(code_, buf, sizeof(buf)); + std::string tmp(context_); + tmp += ": "; + tmp += buf; + what_.reset(new std::string(tmp)); + return what_->c_str(); + } + catch (std::exception&) + { + return "asio system_exception"; + } +#else + try + { + char buf[256] = ""; + std::string tmp(context_); + tmp += ": "; + tmp += strerror_r(code_, buf, sizeof(buf)); + what_.reset(new std::string(tmp)); + return what_->c_str(); + } + catch (std::exception&) + { + return "asio system_exception"; + } +#endif + } + + /// Get the implementation-defined context associated with the exception. + const std::string& context() const + { + return context_; + } + + /// Get the implementation-defined code associated with the exception. + int code() const + { + return code_; + } + +private: + // The context associated with the error. + std::string context_; + + // The code associated with the error. + int code_; + + // The string representation of the error. + mutable boost::scoped_ptr what_; +}; + +/// Output the string associated with a system exception. +/** + * Used to output a human-readable string that is associated with a system + * exception. + * + * @param os The output stream to which the string will be written. + * + * @param e The exception to be written. + * + * @return The output stream. + * + * @relates boost::asio::system_exception + */ +#if BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564)) +std::ostream& operator<<(std::ostream& os, const system_exception& e) +{ + os << e.what(); + return os; +} +#else // BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564)) +template +Ostream& operator<<(Ostream& os, const system_exception& e) +{ + os << e.what(); + return os; +} +#endif // BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564)) + +} // namespace asio +} // namespace boost + +#include + +#endif // BOOST_ASIO_SYSTEM_EXCEPTION_HPP diff --git a/include/boost/asio/time_traits.hpp b/include/boost/asio/time_traits.hpp new file mode 100644 index 00000000..29adfb57 --- /dev/null +++ b/include/boost/asio/time_traits.hpp @@ -0,0 +1,79 @@ +// +// time_traits.hpp +// ~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2005 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_TIME_TRAITS_HPP +#define BOOST_ASIO_TIME_TRAITS_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include + +#include // Must come before posix_time. + +#include +#include +#include + +namespace boost { +namespace asio { + +/// Time traits suitable for use with the deadline timer. +template +struct time_traits; + +/// Time traits specialised for posix_time. +template <> +struct time_traits +{ + /// The time type. + typedef boost::posix_time::ptime time_type; + + /// The duration type. + typedef boost::posix_time::time_duration duration_type; + + /// Get the current time. + static time_type now() + { + return boost::posix_time::microsec_clock::universal_time(); + } + + /// Add a duration to a time. + static time_type add(const time_type& t, const duration_type& d) + { + return t + d; + } + + /// Subtract one time from another. + static duration_type subtract(const time_type& t1, const time_type& t2) + { + return t1 - t2; + } + + /// Convert to UTC in the posix time type. + static boost::posix_time::ptime to_utc(const time_type& t) + { + return t; + } + + /// Convert from UTC in the posix time type. + static time_type from_utc(const boost::posix_time::ptime& t) + { + return t; + } +}; + +} // namespace asio +} // namespace boost + +#include + +#endif // BOOST_ASIO_TIME_TRAITS_HPP diff --git a/include/boost/asio/write.hpp b/include/boost/asio/write.hpp new file mode 100644 index 00000000..07ac7cc5 --- /dev/null +++ b/include/boost/asio/write.hpp @@ -0,0 +1,319 @@ +// +// write.hpp +// ~~~~~~~~~ +// +// Copyright (c) 2003-2005 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_WRITE_HPP +#define BOOST_ASIO_WRITE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include + +#include +#include +#include +#include + +namespace boost { +namespace asio { + +/** + * @defgroup write boost::asio::write + */ +/*@{*/ + +/// Write all of the supplied data to a stream before returning. +/** + * This function is used to write a certain number of bytes of data to a stream. + * The call will block until one of the following conditions is true: + * + * @li All of the data in the supplied buffers has been written. That is, the + * bytes transferred is equal to the sum of the buffer sizes. + * + * @li An error occurred. + * + * This operation is implemented in terms of one or more calls to the stream's + * write_some function. + * + * @param s The stream to which the data is to be written. The type must support + * the Sync_Write_Stream concept. + * + * @param buffers One or more buffers containing the data to be written. The sum + * of the buffer sizes indicates the maximum number of bytes to write to the + * stream. + * + * @returns The number of bytes transferred. + * + * @throws Sync_Write_Stream::error_type Thrown on failure. + * + * @par Example: + * To write a single data buffer use the @ref buffer function as follows: + * @code boost::asio::write(s, boost::asio::buffer(data, size)); @endcode + * See the @ref buffer documentation for information on writing multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + * + * @note This overload is equivalent to calling: + * @code boost::asio::write( + * s, buffers, + * boost::asio::transfer_all(), + * boost::asio::throw_error()); @endcode + */ +template +std::size_t write(Sync_Write_Stream& s, const Const_Buffers& buffers); + +/// Write a certain amount of data to a stream before returning. +/** + * This function is used to write a certain number of bytes of data to a stream. + * The call will block until one of the following conditions is true: + * + * @li All of the data in the supplied buffers has been written. That is, the + * bytes transferred is equal to the sum of the buffer sizes. + * + * @li The completion_condition function object returns true. + * + * This operation is implemented in terms of one or more calls to the stream's + * write_some function. + * + * @param s The stream to which the data is to be written. The type must support + * the Sync_Write_Stream concept. + * + * @param buffers One or more buffers containing the data to be written. The sum + * of the buffer sizes indicates the maximum number of bytes to write to the + * stream. + * + * @param completion_condition The function object to be called to determine + * whether the write operation is complete. The signature of the function object + * must be: + * @code bool completion_condition( + * const Sync_Write_Stream::error_type& error, // Result of latest write_some + * // operation. + * + * std::size_t bytes_transferred // Number of bytes transferred + * // so far. + * ); @endcode + * A return value of true indicates that the write operation is complete. False + * indicates that further calls to the stream's write_some function are + * required. + * + * @returns The number of bytes transferred. + * + * @throws Sync_Write_Stream::error_type Thrown on failure. + * + * @par Example: + * To write a single data buffer use the @ref buffer function as follows: + * @code boost::asio::write(s, boost::asio::buffer(data, size), + * boost::asio::transfer_at_least(32)); @endcode + * See the @ref buffer documentation for information on writing multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + * + * @note This overload is equivalent to calling: + * @code boost::asio::write( + * s, buffers, + * completion_condition, + * boost::asio::throw_error()); @endcode + */ +template +std::size_t write(Sync_Write_Stream& s, const Const_Buffers& buffers, + Completion_Condition completion_condition); + +/// Write a certain amount of data to a stream before returning. +/** + * This function is used to write a certain number of bytes of data to a stream. + * The call will block until one of the following conditions is true: + * + * @li All of the data in the supplied buffers has been written. That is, the + * bytes transferred is equal to the sum of the buffer sizes. + * + * @li The completion_condition function object returns true. + * + * This operation is implemented in terms of one or more calls to the stream's + * write_some function. + * + * @param s The stream to which the data is to be written. The type must support + * the Sync_Write_Stream concept. + * + * @param buffers One or more buffers containing the data to be written. The sum + * of the buffer sizes indicates the maximum number of bytes to write to the + * stream. + * + * @param completion_condition The function object to be called to determine + * whether the write operation is complete. The signature of the function object + * must be: + * @code bool completion_condition( + * const Sync_Write_Stream::error_type& error, // Result of latest write_some + * // operation. + * + * std::size_t bytes_transferred // Number of bytes transferred + * // so far. + * ); @endcode + * A return value of true indicates that the write operation is complete. False + * indicates that further calls to the stream's write_some function are + * required. + * + * @param error_handler The handler to be called when an error occurs. Copies + * will be made of the handler as required. The function signature of the + * handler must be: + * @code void error_handler( + * const Sync_Write_Stream::error_type& error // Result of operation. + * ); @endcode + * + * @returns The number of bytes written. If an error occurs, and the error + * handler does not throw an exception, returns the total number of bytes + * successfully transferred prior to the error. + */ +template +std::size_t write(Sync_Write_Stream& s, const Const_Buffers& buffers, + Completion_Condition completion_condition, Error_Handler error_handler); + +/*@}*/ +/** + * @defgroup async_write boost::asio::async_write + */ +/*@{*/ + +/// Start an asynchronous operation to write of all of the supplied data to a +/// stream. +/** + * This function is used to asynchronously write a certain number of bytes of + * data to a stream. The function call always returns immediately. The + * asynchronous operation will continue until one of the following conditions + * is true: + * + * @li All of the data in the supplied buffers has been written. That is, the + * bytes transferred is equal to the sum of the buffer sizes. + * + * @li An error occurred. + * + * This operation is implemented in terms of one or more calls to the stream's + * async_write_some function. + * + * @param s The stream to which the data is to be written. The type must support + * the Async_Write_Stream concept. + * + * @param buffers One or more buffers containing the data to be written. + * Although the buffers object may be copied as necessary, ownership of the + * underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the handler is called. + * + * @param handler The handler to be called when the write operation completes. + * Copies will be made of the handler as required. The function signature of + * the handler must be: + * @code void handler( + * const Async_Write_Stream::error_type& error, // Result of operation. + * + * std::size_t bytes_transferred // Number of bytes written + * // from the buffers. If an + * // error occurred, this will + * // be less than the sum of the + * // buffer sizes. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation of + * the handler will be performed in a manner equivalent to using + * boost::asio::demuxer::post(). + * + * @par Example: + * To write a single data buffer use the @ref buffer function as follows: + * @code + * boost::asio::async_write(s, boost::asio::buffer(data, size), handler); + * @endcode + * See the @ref buffer documentation for information on writing multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ +template +void async_write(Async_Write_Stream& s, const Const_Buffers& buffers, + Handler handler); + +/// Start an asynchronous operation to write a certain amount of data to a +/// stream. +/** + * This function is used to asynchronously write a certain number of bytes of + * data to a stream. The function call always returns immediately. The + * asynchronous operation will continue until one of the following conditions + * is true: + * + * @li All of the data in the supplied buffers has been written. That is, the + * bytes transferred is equal to the sum of the buffer sizes. + * + * @li The completion_condition function object returns true. + * + * This operation is implemented in terms of one or more calls to the stream's + * async_write_some function. + * + * @param s The stream to which the data is to be written. The type must support + * the Async_Write_Stream concept. + * + * @param buffers One or more buffers containing the data to be written. + * Although the buffers object may be copied as necessary, ownership of the + * underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the handler is called. + * + * @param completion_condition The function object to be called to determine + * whether the write operation is complete. The signature of the function object + * must be: + * @code bool completion_condition( + * const Async_Write_Stream::error_type& error, // Result of latest write_some + * // operation. + * + * std::size_t bytes_transferred // Number of bytes transferred + * // so far. + * ); @endcode + * A return value of true indicates that the write operation is complete. False + * indicates that further calls to the stream's async_write_some function are + * required. + * + * @param handler The handler to be called when the write operation completes. + * Copies will be made of the handler as required. The function signature of the + * handler must be: + * @code void handler( + * const Async_Write_Stream::error_type& error, // Result of operation. + * + * std::size_t bytes_transferred // Number of bytes written + * // from the buffers. If an + * // error occurred, this will + * // be less than the sum of the + * // buffer sizes. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation of + * the handler will be performed in a manner equivalent to using + * boost::asio::demuxer::post(). + * + * @par Example: + * To write a single data buffer use the @ref buffer function as follows: + * @code boost::asio::async_write(s, + * boost::asio::buffer(data, size), + * boost::asio::transfer_at_least(32), + * handler); @endcode + * See the @ref buffer documentation for information on writing multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ +template +void async_write(Async_Write_Stream& s, const Const_Buffers& buffers, + Completion_Condition completion_condition, Handler handler); + +/*@}*/ + +} // namespace asio +} // namespace boost + +#include + +#include + +#endif // BOOST_ASIO_WRITE_HPP diff --git a/include/includes.h b/include/includes.h new file mode 100644 index 00000000..df7dc98a --- /dev/null +++ b/include/includes.h @@ -0,0 +1,50 @@ +#ifndef _SL_INCLUDES_ +#define _SL_INCLUDES_ + +#include +#include +#include +#include +#include +#include +#include + +// For debugging +#include +#include + +#include +#include +#include +#include +#include + +int httoi(const char* value); + +#define SL_BUFFER_SIZE 8192 + +// Data types +#define byte unsigned char + +struct LLUUID { + byte data[16]; + LLUUID operator=(const int p) { for (size_t i = 0; i < 16; i++) { data[i] = (byte)p; } return *this; }; + // This needs to loop through the string and convert each letter to a byte using (byte)httoi() + //LLUUID operator=(const std::string p) { memcpy(data, p.c_str(), 16); return *this; }; +}; + +typedef unsigned int U32; +// + +#ifdef WIN32 +#pragma warning (disable : 4996 4251) +#ifdef LIBSECONDLIFE_EXPORTS + #define LIBSECONDLIFE_CLASS_DECL __declspec(dllexport) +#else + #define LIBSECONDLIFE_CLASS_DECL __declspec(dllimport) +#endif +#else +#define LIBSECONDLIFE_CLASS_DECL +#endif + +#endif //_SL_INCLUDES_ diff --git a/include/mainpage.h b/include/mainpage.h new file mode 100644 index 00000000..b6746b48 --- /dev/null +++ b/include/mainpage.h @@ -0,0 +1,22 @@ +/** @mainpage libsecondlife + * + * @authors Second Life Reverse Engineering Team + * + * @section intro Introduction + * This package provides [FIXME] + * All header files for external access are located in the include directory. + * Header files that are not meant for external access reside in the src + * directory. Source files are located in the src directory. Windows project + * files are in the win32 directory. + * + *
+ * @section notes notes + * [FIXME] + *
+ * @section requirements requirements + * [FIXME] + *
+ * @todo Everything + * @todo The kitchen sink + * + */ diff --git a/project-root.jam b/project-root.jam new file mode 100644 index 00000000..e69de29b diff --git a/src/Decoder.cpp b/src/Decoder.cpp new file mode 100644 index 00000000..6ecf9bd6 --- /dev/null +++ b/src/Decoder.cpp @@ -0,0 +1,11 @@ +#include "Decoder.h" + +Decoder::Decoder() +{ + ; +} + +Decoder::~Decoder() +{ + ; +} diff --git a/src/Network.cpp b/src/Network.cpp new file mode 100644 index 00000000..f397fb6c --- /dev/null +++ b/src/Network.cpp @@ -0,0 +1,125 @@ +#include "Network.h" + +Network::Network(ProtocolManager* protocol) +{ + _protocol = protocol; + _currentSim = NULL; + _avatar_id = 0; + _session_id = 0; + _secure_session_id = 0; +} + +Network::~Network() +{ + //FIXME: Close and free all the sockets + // Delete all remaining packets +} + +void Network::receivePacket(const boost::asio::error& error, std::size_t length, char* receiveBuffer) +{ + // Debug + printf("Received datagram, length: %u\n", length); + for (size_t i = 0; i < length; i++) { + printf("%02x ", receiveBuffer[i]); + } + printf("\n"); + + // Build a Packet object and fill it with the incoming data + Packet* packet = new Packet(); + packet->setRawData((byte*)receiveBuffer, length); + + // Push it on to the list + boost::mutex::scoped_lock lock(_inboxMutex); + _inbox.push_back(packet); + lock.unlock(); +} + +int Network::connectSim(boost::asio::ipv4::address ip, unsigned short port, U32 code, bool setCurrent) +{ + // Check if we are already connected to this sim + for (size_t i = 0; i < _connections.size(); i++) { + if (ip == _connections[i]->ip() && port == _connections[i]->port()) { + //FIXME: Log + return -1; + } + } + + // Build a connection packet + Packet* packet = new Packet(_protocol, 36); + packet->setCommand("UseCircuitCode"); + packet->setField("CircuitCode", 1, "ID", &_session_id); + packet->setField("CircuitCode", 1, "SessionID", &_session_id); + packet->setField("CircuitCode", 1, "Code", &code); + + // Create the SimConnection + SimConnection* sim = new SimConnection(ip, port, code); + if (setCurrent) _currentSim = sim; + + boost::asio::datagram_socket* socket = new boost::asio::datagram_socket(_demuxer, boost::asio::ipv4::udp::endpoint(0)); + sim->socket(socket); + + // Send the packet + try { + size_t bytesSent = socket->send_to(boost::asio::buffer(packet->getRawDataPtr(), packet->getLength()), 0, sim->endpoint()); + // Debug + printf("Sent %i byte connection packet\n", bytesSent); + + delete packet; + } catch (boost::asio::error& e) { + delete packet; + + // Debug + std::cerr << e << std::endl; + + return -2; + } + + // Start listening on this socket + while (sim && sim->running()) { + socket->async_receive(boost::asio::buffer(sim->buffer(), sim->bufferSize()), 0, + boost::bind(&Network::receivePacket, this, boost::asio::placeholders::error, + boost::asio::placeholders::bytes_transferred, sim->buffer())); + _demuxer.run(); + _demuxer.reset(); + } + + // Debug + printf("Closed connection to %u\n", (unsigned int)sim); + + return 0; +} + +int Network::sendPacket(boost::asio::ipv4::address ip, unsigned short port, Packet* packet) +{ + //boost::asio::ipv4::udp::endpoint sim; + bool found = false; + size_t i; + + // Check if we are connected to this sim + for (i = 0; i < _connections.size(); i++) { + if (ip == _connections[i]->ip() && port == _connections[i]->port()) { + //sim = _connections[i]->endpoint(); + found = true; + break; + } + } + + if (!found) { + //FIXME: Log + return -1; + } + + try { + size_t bytesSent = _connections[i]->socket()->send_to(boost::asio::buffer(packet->getRawDataPtr(), packet->getLength()), 0, _connections[i]->endpoint()); + + // Debug + printf("Sent %i bytes\n", bytesSent); + } catch (boost::asio::error& e) { + // Debug + std::cerr << e << std::endl; + + return -2; + } + + return 0; +} diff --git a/src/Packet.cpp b/src/Packet.cpp new file mode 100644 index 00000000..479fae90 --- /dev/null +++ b/src/Packet.cpp @@ -0,0 +1,180 @@ +#include "Packet.h" + +Packet::Packet() +{ + _buffer = (byte*)malloc(DEFAULT_PACKET_SIZE); + if (!_buffer) { + //FIXME: Log memory error + } + + _length = 0; + _protocol = NULL; +} + +Packet::Packet(ProtocolManager* protocol, size_t length) +{ + _buffer = (byte*)malloc(length ? length : DEFAULT_PACKET_SIZE); + if (!_buffer) { + //FIXME: Log memory error + _length = 0; + } else { + _length = length; + } + + _protocol = protocol; +} + +Packet::~Packet() +{ + free(_buffer); +} + +bool Packet::setCommand(std::string command) +{ + packetDiagram* layout = _protocol->getCommand(command); + if (!layout) return false; + + _layout = layout; + return true; +} + +ll::llType Packet::getFieldType(std::string block, std::string field) +{ + std::list::iterator i; + + for (i = _layout->blocks.begin(); i != _layout->blocks.end(); ++i) { + if ((*i)->name == block) { + packetBlock* block = (*i); + + std::list::iterator j; + + for (j = block->fields.begin(); j != block->fields.end(); ++j) { + if ((*j)->name == field) { + return (*j)->type; + } + } + } + + //FIXME: Log + return ll::INVALID_TYPE; + } + + //FIXME: Log + return ll::INVALID_TYPE; +} + +void* Packet::getField(std::string block, size_t blockNumber, std::string field) +{ + // Check how many blocks this field can hold, and if blockNumber is in range + int frequency = _protocol->getBlockFrequency(_layout, block); + if (frequency != -1 && (int)blockNumber > frequency) { + // blockNumber is out of range + //FIXME: Log + return NULL; + } + + // Find the total offset for the field + size_t blockSize = _protocol->getBlockSize(_layout, block); + if (!blockSize) { + //FIXME: Log + return NULL; + } + + int fieldOffset = _protocol->getFieldOffset(_layout, block, field); + if (fieldOffset < 0) { + //FIXME: Log + return NULL; + } + // + + return (void*)(_buffer + blockSize * blockNumber + fieldOffset); +} + +int Packet::setField(std::string block, size_t blockNumber, std::string field, void* value) +{ + if (!_layout) { + //FIXME: Log + return -1; + } + + // Find out what type of field this is + ll::llType type = getFieldType(block, field); + + // Check how many blocks this field can hold, and if blockNumber is in range + int frequency = _protocol->getBlockFrequency(_layout, block); + if (blockNumber <= 0 || (frequency != -1 && (int)blockNumber > frequency)) { + // blockNumber is out of range + //FIXME: Log + return -2; + } + + // Find the total offset for the field + size_t blockSize = _protocol->getBlockSize(_layout, block); + if (!blockSize) { + //FIXME: Log + return -3; + } + + int fieldOffset = _protocol->getFieldOffset(_layout, block, field); + if (fieldOffset < 0) { + //FIXME: Log + return -4; + } + + size_t fieldSize = _protocol->getTypeSize(type); + size_t offset = blockSize * (blockNumber - 1) + fieldOffset; + + // Reallocate memory if necessary + if ((offset + fieldSize) > _length) { + if ((offset + fieldSize) > DEFAULT_PACKET_SIZE) { + _buffer = (byte*)realloc(_buffer, offset + fieldSize); + if (!_buffer) { + //FIXME: Log memory error + _length = 0; + return -5; + } + } + + _length = offset + fieldSize; + } + + // Write the actual value + memcpy(_buffer + offset, value, fieldSize); + + return 0; +} + +int Packet::getRawData(byte* buffer, size_t length) +{ + return (memcpy((void*)buffer, _buffer, length) != NULL); +} + +byte* Packet::getRawDataPtr() +{ + return _buffer; +} + +void Packet::setRawData(byte* buffer, size_t length) +{ + if (length > _length) { + _buffer = (byte*)realloc(_buffer, length); + if (!_buffer) { + //FIXME: Log memory error + _length = 0; + return; + } + } + + memcpy(_buffer, buffer, length); + _length = length; +} + +boost::asio::ipv4::udp::endpoint Packet::getRemoteHost() +{ + return _remoteHost; +} + +void Packet::setRemoteHost(boost::asio::ipv4::udp::endpoint remoteHost) +{ + _remoteHost = remoteHost; +} diff --git a/src/ProtocolManager.cpp b/src/ProtocolManager.cpp new file mode 100644 index 00000000..45329ba8 --- /dev/null +++ b/src/ProtocolManager.cpp @@ -0,0 +1,498 @@ +#include "ProtocolManager.h" + +// Trim an ISO C++ string by Marco Dorantes +std::string trim(std::string &s, const std::string &drop = " ") +{ + std::string r = s.erase(s.find_last_not_of(drop) + 1); + return r.erase(0, r.find_first_not_of(drop)); +} + +bool getBlockMarkers(const char* buffer, size_t &start, size_t &end, size_t &children) +{ + size_t startBlock = 0; + size_t depth = 0; + + children = 0; + + for (size_t i = start; i <= end; i++) { + if (buffer[i] == '{') { + depth++; + + if (depth == 1) { + startBlock = i; + } else if (depth == 2 && !children) { + children = i; + } + } else if (buffer[i] == '}') { + depth--; + + if (depth == 0 && startBlock) { + start = startBlock; + end = i; + + return true; + } + } + } + + //FIXME: Log + return false; +} + +ProtocolManager::ProtocolManager() +{ + ; +} + +ProtocolManager::~ProtocolManager() +{ + //FIXME: Apparently the linked lists don't automatically destroy the objects + // it has pointers for, so we need to iterate through the entire map + // and free memory. +} + +void ProtocolManager::printMap() +{ + size_t i; + std::list::iterator j; + std::list::iterator k; + + for (i = 0; i < 65536; i++) { + if (_lowPackets[i].name.length()) { + printf("Low %05u - %s - %s - %s\n", i, _lowPackets[i].name.c_str(), + _lowPackets[i].trusted ? "Trusted" : "Untrusted", + _lowPackets[i].encoded ? "Unencoded" : "Zerocoded"); + + for (j = _lowPackets[i].blocks.begin(); j != _lowPackets[i].blocks.end(); ++j) { + printf("\t%04u %s (%02i)\n", (*j)->keywordPosition, (*j)->name.c_str(), (*j)->frequency); + + for (k = (*j)->fields.begin(); k != (*j)->fields.end(); ++k) { + printf("\t\t%04u %s (%s)\n", (*k)->keywordPosition, (*k)->name.c_str(), getTypeName((*k)->type).c_str()); + } + } + } + } + + for (i = 0; i < 256; i++) { + if (_mediumPackets[i].name.length()) { + printf("Medium %05u - %s - %s - %s\n", i, _mediumPackets[i].name.c_str(), + _mediumPackets[i].trusted ? "Trusted" : "Untrusted", + _mediumPackets[i].encoded ? "Unencoded" : "Zerocoded"); + + for (j = _mediumPackets[i].blocks.begin(); j != _mediumPackets[i].blocks.end(); ++j) { + printf("\t%04u %s (%02i)\n", (*j)->keywordPosition, (*j)->name.c_str(), (*j)->frequency); + + for (k = (*j)->fields.begin(); k != (*j)->fields.end(); ++k) { + printf("\t\t%04u %s (%s)\n", (*k)->keywordPosition, (*k)->name.c_str(), getTypeName((*k)->type).c_str()); + } + } + } + } + + for (i = 0; i < 256; i++) { + if (_highPackets[i].name.length()) { + printf("High %05u - %s - %s - %s\n", i, _highPackets[i].name.c_str(), + _highPackets[i].trusted ? "Trusted" : "Untrusted", + _highPackets[i].encoded ? "Unencoded" : "Zerocoded"); + + for (j = _highPackets[i].blocks.begin(); j != _highPackets[i].blocks.end(); ++j) { + printf("\t%04u %s (%02i)\n", (*j)->keywordPosition, (*j)->name.c_str(), (*j)->frequency); + + for (k = (*j)->fields.begin(); k != (*j)->fields.end(); ++k) { + printf("\t\t%04u %s (%s)\n", (*k)->keywordPosition, (*k)->name.c_str(), getTypeName((*k)->type).c_str()); + } + } + } + } +} + +bool ProtocolManager::getFields(packetBlock* block, std::string protocolMap, size_t start, size_t end) +{ + size_t fieldStart = start; + size_t fieldEnd = end; + size_t children = 0; + packetField* field; + + while(getBlockMarkers(protocolMap.c_str(), fieldStart, fieldEnd, children)) { + if (children) { + //FIXME: Log + return false; + } + + std::string temp = protocolMap.substr(fieldStart + 1, (fieldEnd - 1) - fieldStart); + temp = trim(temp); + field = new packetField(); + + size_t delimiter = temp.find_first_of(" "); + if (delimiter == std::string::npos) { + //FIXME: Log + return false; + } + + // Get the field name + field->name = temp.substr(0, delimiter); + + // Get the keyword position + field->keywordPosition = getKeywordPosition(field->name); + + // Get the field type + temp = temp.substr(delimiter + 1, temp.length() - delimiter - 1); + field->type = getFieldType(temp); + + // Add this field to the linked list + block->fields.push_back(field); + + fieldStart = fieldEnd + 1; + fieldEnd = end; + } + + // Sort the fields based on the keyword position + using namespace std; + block->fields.sort(greater()); + + return true; +} + +bool ProtocolManager::getBlocks(packetDiagram* packet, std::string protocolMap, size_t start, size_t end) +{ + size_t blockStart = start; + size_t blockEnd = end; + size_t children = 0; + packetBlock* block; + + while (getBlockMarkers(protocolMap.c_str(), blockStart, blockEnd, children)) { + std::string temp = protocolMap.substr(blockStart + 1, (children ? children - 1 : blockEnd - 1) - blockStart); + temp = trim(temp); + std::stringstream stream(temp); + std::vector block_tokens; + block = new packetBlock(); + + while (stream >> temp) { + block_tokens.push_back(temp); + } + + // Get the block name + block->name = block_tokens.at(0); + + // Find the frequency of this block (-1 for variable, 1 for single) + temp = block_tokens.at(1); + if (temp == "Variable") { + block->frequency = -1; + } else if (temp == "Single") { + block->frequency = 1; + } else if (temp == "Multiple") { + std::istringstream int_stream(block_tokens.at(2)); + int_stream >> block->frequency; + } else { + block->frequency = 5; + } + + // Get the keyword position of this block + block->keywordPosition = getKeywordPosition(block->name); + + // Add this block to the linked list + packet->blocks.push_back(block); + + // Populate the fields linked list + getFields(block, protocolMap, blockStart + 1, blockEnd - 1); + + blockStart = blockEnd + 1; + blockEnd = end; + } + + // Sort the blocks based on the keyword position + using namespace std; + packet->blocks.sort(greater()); + + return true; +} + +int ProtocolManager::loadKeywords(std::string filename) +{ + std::ifstream input(filename.c_str()); + std::string line; + int i = 0; + + if (!input.is_open()) { + //FIXME: Log + return -1; + } + + while (!input.eof()) { + getline(input, line); + line = trim(line, "\r"); + _keywordMap[line] = i++; + } + + input.close(); + return 0; +} + +int ProtocolManager::decryptCommFile(std::string source, std::string destination) +{ + byte magicKey = 0; + byte buffer[2048]; + size_t nread; + + FILE* commFile = fopen(source.c_str(), "rb"); + if (!commFile) { + //FIXME: Debug log this + return -1; + } + + FILE* output = fopen(destination.c_str(), "wb"); + if (!output) { + //FIXME: Debug log + return -2; + } + + while ((nread = fread(buffer, sizeof(char), sizeof(buffer), commFile)) > 0) { + // Decryption + for (size_t i = 0; i < nread; i++) + { + buffer[i] ^= magicKey; + magicKey += 43; + } + + fwrite(buffer, sizeof(char), nread, output); + } + + fclose(commFile); + fclose(output); + + return 0; +} + +int ProtocolManager::getKeywordPosition(std::string keyword) +{ + std::map::iterator result; + + result = _keywordMap.find(keyword); + if (result == _keywordMap.end()) { + //FIXME: Log + return -1; + } + + return result->second; +} + +int ProtocolManager::buildProtocolMap(std::string filename) +{ + std::string protocolMap; + byte buffer[2048]; + size_t nread; + packetDiagram* packet; + size_t end; + size_t cmdStart = 0; + size_t cmdEnd; + size_t cmdChildren = 0; + size_t low = 1; + size_t medium = 1; + size_t high = 1; + + FILE* input = fopen(filename.c_str(), "rb"); + if (!input) { + //FIXME: Debug log + return -1; + } + + // Read the file in to memory + while ((nread = fread(buffer, sizeof(char), sizeof(buffer), input)) > 0) { + /*if (nread != BUFFER_SIZE) { + buffer[nread] = '\0'; + } else { + buffer[BUFFER_SIZE] = '\0'; + }*/ + + protocolMap.append((const char*)&buffer, nread); + } + + cmdEnd = end = protocolMap.length(); + + while (getBlockMarkers(protocolMap.c_str(), cmdStart, cmdEnd, cmdChildren)) { + std::string temp = protocolMap.substr(cmdStart + 1, (cmdChildren ? cmdChildren - 1 : cmdEnd - 1) - cmdStart); + temp = trim(temp); + std::stringstream header(temp); + std::vector header_tokens; + + while (header >> temp) { + header_tokens.push_back(temp); + } + + // Get the frequency first so we know where to put this command + temp = header_tokens.at(1); + if (temp == "Fixed") { + // Get the fixed position + temp = header_tokens.at(2); + // Truncate it to a short + int fixed = httoi((char*)temp.c_str()) ^ 0xffff0000; + packet = &_lowPackets[fixed]; + } else if (temp == "Low") { + packet = &_lowPackets[low++]; + } else if (temp == "Medium") { + packet = &_mediumPackets[medium++]; + } else if (temp == "High") { + packet = &_highPackets[high++]; + } else { + //FIXME: Debug log + return -2; + } + + // Get the command name + packet->name = header_tokens.at(0); + + // Trusted? + packet->trusted = (header_tokens.at(2) == "Trusted"); + + // Encoded? + packet->encoded = (header_tokens.at(3) == "Zerocoded"); + + // Get the blocks + getBlocks(packet, protocolMap, cmdStart + 1, cmdEnd - 1); + + // Increment our position in protocol map + cmdStart = cmdEnd + 1; + cmdEnd = end; + } + + fclose(input); + + return 0; +} + +packetDiagram* ProtocolManager::getCommand(std::string command) +{ + size_t i; + + for (i = 0; i < 65536; i++) { + if (_lowPackets[i].name == command) return &_lowPackets[i]; + } + + for (i = 0; i < 255; i++) { + if (_mediumPackets[i].name == command) return &_mediumPackets[i]; + } + + for (i = 0; i < 255; i++) { + if (_highPackets[i].name == command) return &_highPackets[i]; + } + + return NULL; +} + +ll::llType ProtocolManager::getFieldType(std::string type) +{ + const std::string llTypes[] = {"U8", "U16", "U32", "U64", "S8", "S16", "S32", "S64", + "F8", "F16", "F32", "F64", "LLUUID", "BOOL", "LLVector3", + "LLVector3d", "LLQuaternion", "IPADDR", "IPPORT", + "Variable", "Fixed", "Single", "Multiple", ""}; + int i = 0; + + while (llTypes[i].length()) { + if (type == llTypes[i]) { + return (ll::llType)i; + } + + i++; + } + + //FIXME: Log + return ll::INVALID_TYPE; +} + +int ProtocolManager::getTypeSize(ll::llType type) +{ + // U8, U16, U32, U64, S8, S16, S32, S64, F8, F16, F32, F64, LLUUID, BOOL, llVector3, + // llVector3d, llQuaternion, IPADDR, IPPORT, Variable + const int sizes[] = {1, 2, 4, 8, 1, 2, 4, 8, 1, 2, 4, 8, 16, 1, sizeof(llVector3), + sizeof(llVector3d), sizeof(llQuaternion), 4, 2, -1}; + + if (type < 0 || type > 19) { + //FIXME: Log + return 0; + } else { + return sizes[type]; + } +} + +std::string ProtocolManager::getTypeName(ll::llType type) +{ + std::string typeName; + std::string names[] = {"U8", "U16", "U32", "U64", "S8", "S16", "S32", "S64", "F8", "F16", "F32", + "F64", "LLUUID", "BOOL", "llVector3", "llVector3d", "llQuaternion", "IPADDR", + "IPPORT", "Variable"}; + + if (type < 0 || type > 19) { + //FIXME: Log + typeName = "Invalid"; + } else { + typeName = names[type]; + } + + return typeName; +} + +int ProtocolManager::getBlockFrequency(packetDiagram* layout, std::string block) +{ + std::list::iterator i; + + for ( i = layout->blocks.begin(); i != layout->blocks.end(); ++i) { + if ((*i)->name == block) { + return (*i)->frequency; + } + } + + //FIXME: Log + return 0; +} + +size_t ProtocolManager::getBlockSize(packetDiagram* layout, std::string block) +{ + std::list::iterator i; + + for (i = layout->blocks.begin(); i != layout->blocks.end(); ++i) { + if ((*i)->name == block) { + packetBlock* block = (*i); + size_t size = 0; + + std::list::iterator j; + + for (j = block->fields.begin(); j != block->fields.end(); ++j) { + size += getTypeSize((*j)->type); + } + + return size; + } + } + + //FIXME: Log + return 0; +} + +int ProtocolManager::getFieldOffset(packetDiagram* layout, std::string block, std::string field) +{ + std::list::iterator i; + + for (i = layout->blocks.begin(); i != layout->blocks.end(); ++i) { + if ((*i)->name == block) { + packetBlock* block = (*i); + int offset = 0; + + std::list::iterator j; + + for (j = block->fields.begin(); j != block->fields.end(); ++j) { + if ((*j)->name == field) { + return offset; + } else { + offset += (int)getTypeSize((*j)->type); + } + } + + // The block didn't have the field we're looking for + //FIXME: Log + return -1; + } + } + + //FIXME: Log + return -2; +} diff --git a/src/SecondLife.cpp b/src/SecondLife.cpp new file mode 100644 index 00000000..34944244 --- /dev/null +++ b/src/SecondLife.cpp @@ -0,0 +1,16 @@ +#include "SecondLife.h" + +SecondLife::SecondLife() +{ + boost::thread _placeHolder; + _protocol = new ProtocolManager(); + _network = new Network(_protocol); + _decoder = new Decoder(); +} + +SecondLife::~SecondLife() +{ + delete _protocol; + delete _network; + delete _decoder; +} diff --git a/src/SimConnection.cpp b/src/SimConnection.cpp new file mode 100644 index 00000000..734a159f --- /dev/null +++ b/src/SimConnection.cpp @@ -0,0 +1,34 @@ +#include "SimConnection.h" + +SimConnection::SimConnection() +{ + _code = 0; + _socket = NULL; + _running = true; + + _buffer = (char*)malloc(SL_BUFFER_SIZE); +} + +SimConnection::SimConnection(boost::asio::ipv4::address ip, unsigned short port, U32 code) +{ + _endpoint = boost::asio::ipv4::udp::endpoint(port, ip); + _code = code; + _socket = NULL; + _running = true; + + _buffer = (char*)malloc(SL_BUFFER_SIZE); +} + +SimConnection::~SimConnection() +{ + delete _socket; + free(_buffer); +} + +bool SimConnection::operator==(SimConnection &p) { + return (_endpoint == p.endpoint()); +} + +bool SimConnection::operator!=(SimConnection &p) { + return !(*this == p); +} diff --git a/src/functions.cpp b/src/functions.cpp new file mode 100644 index 00000000..7200fc9d --- /dev/null +++ b/src/functions.cpp @@ -0,0 +1,59 @@ +#include "includes.h" + +// Convert a "hex string" to an integer by Anders Molin +int httoi(const char* value) +{ + struct HEXMAP + { + byte c; + int value; + }; + + const int nHexMap = 16; + + HEXMAP hmLookup[nHexMap] = + { + {'0', 0}, {'1', 1}, + {'2', 2}, {'3', 3}, + {'4', 4}, {'5', 5}, + {'6', 6}, {'7', 7}, + {'8', 8}, {'9', 9}, + {'A', 10}, {'B', 11}, + {'C', 12}, {'D', 13}, + {'E', 14}, {'F', 15} + }; + + const char* s = value; + int result = 0; + + if (*s == '0' && *(s + 1) == 'x') + s += 2; + + bool firsttime = true; + + while (*s != '\0') + { + bool found = false; + + for (int i = 0; i < nHexMap; i++) + { + if (*s == hmLookup[i].c) + { + if (!firsttime) + result <<= 4; + + result |= hmLookup[i].value; + found = true; + break; + } + } + + if (!found) + break; + + s++; + firsttime = false; + } + + return result; +} diff --git a/test_app/Jamfile b/test_app/Jamfile new file mode 100644 index 00000000..0d72d88c --- /dev/null +++ b/test_app/Jamfile @@ -0,0 +1,6 @@ +project test_app + : default-build debug + : build-dir ../bin + ; + +exe test_app : main.cpp ..//secondlife ; diff --git a/test_app/main.cpp b/test_app/main.cpp new file mode 100644 index 00000000..1ee52aae --- /dev/null +++ b/test_app/main.cpp @@ -0,0 +1,52 @@ +#include "SecondLife.h" +#include + +int main() +{ + //Packet* packet; + bool success; + SecondLife* client = new SecondLife(); + + if (client->loadKeywords("keywords.txt") == 0) { + printf("Loaded keyword file\n"); + } else { + printf("Failed to load the keyword file\n"); + + delete client; + return -1; + } + + if (client->decryptCommFile("comm.dat", "output.txt") == 0) { + printf("Decrypted comm file\n"); + } else { + printf("Failed to decrypt the comm file\n"); + } + + if (client->buildProtocolMap("output.txt") == 0) { + printf("Built protocol map\n"); + } else { + printf("Failed to build the protocol map\n"); + + delete client; + return -2; + } + + client->_protocol->printMap(); + + //printf("Building UseCircuitCode packet\n"); + //packet = new Packet(client->_protocol); + //success = packet->setCommand("UseCircuitCode"); + + if (success) { + //byte agentID[16] = {0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F}; + //packet->setField("CircuitCode", 1, "ID", agentID); + boost::asio::ipv4::address address("192.168.0.105"); + client->_network->connectSim(address, 1000, 12345, true); + } else { + printf("Failed to build the AddCircuitCode packet"); + } + + //delete packet; + delete client; + return 0; +}