@@ -4641,12 +4641,10 @@ - (void)doWriteData
4641
4641
4642
4642
// Note: This method is not called if theCurrentWrite is an GCDAsyncSpecialPacket (startTLS packet)
4643
4643
4644
- BOOL waiting = NO ;
4644
+ BOOL waiting = NO ;
4645
4645
NSError *error = nil ;
4646
-
4647
4646
size_t bytesWritten = 0 ;
4648
4647
4649
-
4650
4648
if (flags & kSocketSecure )
4651
4649
{
4652
4650
#if TARGET_OS_IPHONE
@@ -4661,7 +4659,6 @@ - (void)doWriteData
4661
4659
}
4662
4660
4663
4661
CFIndex result = CFWriteStreamWrite (writeStream, (UInt8 *)buffer, (CFIndex)bytesToWrite);
4664
-
4665
4662
LogVerbose (@" CFWriteStreamWrite(%lu ) = %li " , bytesToWrite, result);
4666
4663
4667
4664
if (result < 0 )
@@ -4670,12 +4667,14 @@ - (void)doWriteData
4670
4667
}
4671
4668
else
4672
4669
{
4673
- waiting = YES ;
4674
4670
bytesWritten = (size_t )result;
4671
+
4672
+ // We always set waiting to true in this scenario.
4673
+ // CFStream may have altered our underlying socket to non-blocking.
4674
+ // Thus if we attempt to write without a callback, we may end up blocking our queue.
4675
+ waiting = YES ;
4675
4676
}
4676
4677
4677
- flags &= ~kSocketCanAcceptBytes ;
4678
-
4679
4678
#else
4680
4679
4681
4680
// We're going to use the SSLWrite function.
@@ -4721,7 +4720,10 @@ - (void)doWriteData
4721
4720
4722
4721
OSStatus result;
4723
4722
4724
- if (sslWriteCachedLength > 0 )
4723
+ BOOL hasCachedDataToWrite = (sslWriteCachedLength > 0 );
4724
+ BOOL hasNewDataToWrite = YES ;
4725
+
4726
+ if (hasCachedDataToWrite)
4725
4727
{
4726
4728
size_t processed = 0 ;
4727
4729
@@ -4731,17 +4733,34 @@ - (void)doWriteData
4731
4733
{
4732
4734
bytesWritten = sslWriteCachedLength;
4733
4735
sslWriteCachedLength = 0 ;
4736
+
4737
+ if (currentWrite->bytesDone == [currentWrite->buffer length ])
4738
+ {
4739
+ // We've written all data for the current write.
4740
+ hasNewDataToWrite = NO ;
4741
+ }
4734
4742
}
4735
4743
else
4736
4744
{
4737
- // Handled "below"
4745
+ if (result == errSSLWouldBlock)
4746
+ {
4747
+ waiting = YES ;
4748
+ }
4749
+ else
4750
+ {
4751
+ error = [self sslError: result];
4752
+ }
4753
+
4754
+ // Can't write any new data since we were unable to write the cached data.
4755
+ hasNewDataToWrite = NO ;
4738
4756
}
4739
4757
}
4740
- else
4758
+
4759
+ if (hasNewDataToWrite)
4741
4760
{
4742
- void *buffer = (void *)[currentWrite->buffer bytes ] + currentWrite->bytesDone ;
4761
+ void *buffer = (void *)[currentWrite->buffer bytes ] + currentWrite->bytesDone + bytesWritten ;
4743
4762
4744
- NSUInteger bytesToWrite = [currentWrite->buffer length ] - currentWrite->bytesDone ;
4763
+ NSUInteger bytesToWrite = [currentWrite->buffer length ] - currentWrite->bytesDone - bytesWritten ;
4745
4764
4746
4765
if (bytesToWrite > SIZE_MAX) // NSUInteger may be bigger than size_t (write param 3)
4747
4766
{
@@ -4770,28 +4789,20 @@ - (void)doWriteData
4770
4789
{
4771
4790
if (result == errSSLWouldBlock)
4772
4791
{
4792
+ waiting = YES ;
4773
4793
sslWriteCachedLength = sslBytesToWrite;
4774
4794
}
4795
+ else
4796
+ {
4797
+ error = [self sslError: result];
4798
+ }
4775
4799
4776
4800
keepLooping = NO ;
4777
-
4778
- // Additional handling "below"
4779
4801
}
4780
- }
4781
- }
4782
-
4783
- if (result != noErr) // "Below"
4784
- {
4785
- if (result == errSSLWouldBlock)
4786
- {
4787
- waiting = YES ;
4788
- flags &= ~kSocketCanAcceptBytes ;
4789
- }
4790
- else
4791
- {
4792
- error = [self sslError: result];
4793
- }
4794
- }
4802
+
4803
+ } // while (keepLooping)
4804
+
4805
+ } // if (hasNewDataToWrite)
4795
4806
4796
4807
#endif
4797
4808
}
@@ -4815,30 +4826,47 @@ - (void)doWriteData
4815
4826
if (result < 0 )
4816
4827
{
4817
4828
if (errno == EWOULDBLOCK)
4829
+ {
4818
4830
waiting = YES ;
4831
+ }
4819
4832
else
4833
+ {
4820
4834
error = [self errnoErrorWithReason: @" Error in write() function" ];
4821
-
4822
- flags &= ~kSocketCanAcceptBytes ;
4823
- }
4824
- else if (result == 0 )
4825
- {
4826
- waiting = YES ;
4827
- flags &= ~kSocketCanAcceptBytes ;
4835
+ }
4828
4836
}
4829
4837
else
4830
4838
{
4831
4839
bytesWritten = result;
4832
4840
}
4833
4841
}
4834
4842
4843
+ // We're done with our writing.
4844
+ // If we explictly ran into a situation where the socket told us there was no room in the buffer,
4845
+ // then we immediately resume listening for notifications.
4846
+ //
4847
+ // We must do this before we dequeue another write,
4848
+ // as that may in turn invoke this method again.
4849
+ //
4850
+ // Note that if CFStream is involved, it may have maliciously put our socket in blocking mode.
4851
+
4852
+ if (waiting)
4853
+ {
4854
+ flags &= ~kSocketCanAcceptBytes ;
4855
+
4856
+ if (![self usingCFStream ])
4857
+ {
4858
+ [self resumeWriteSource ];
4859
+ }
4860
+ }
4861
+
4862
+ // Check our results
4863
+
4835
4864
BOOL done = NO ;
4836
4865
4837
4866
if (bytesWritten > 0 )
4838
4867
{
4839
4868
// Update total amount read for the current write
4840
4869
currentWrite->bytesDone += bytesWritten;
4841
-
4842
4870
LogVerbose (@" currentWrite->bytesDone = %lu " , currentWrite->bytesDone );
4843
4871
4844
4872
// Is packet done?
@@ -4854,22 +4882,40 @@ - (void)doWriteData
4854
4882
[self maybeDequeueWrite ];
4855
4883
}
4856
4884
}
4857
- else if (bytesWritten > 0 )
4885
+ else
4858
4886
{
4859
- // We're not done with the entire write, but we have written some bytes
4887
+ // We were unable to finish writing the data,
4888
+ // so we're waiting for another callback to notify us of available space in the lower-level output buffer.
4860
4889
4861
- if (delegateQueue && [delegate respondsToSelector: @selector ( socket:didWritePartialDataOfLength:tag: )] )
4890
+ if (!waiting & !error )
4862
4891
{
4863
- id theDelegate = delegate;
4864
- GCDAsyncWritePacket *theWrite = currentWrite;
4892
+ // This would be the case if our write was able to accept some data, but not all of it.
4865
4893
4866
- dispatch_async (delegateQueue, ^{
4867
- NSAutoreleasePool *pool = [[NSAutoreleasePool alloc ] init ];
4868
-
4869
- [theDelegate socket: self didWritePartialDataOfLength: bytesWritten tag: theWrite->tag];
4894
+ flags &= ~kSocketCanAcceptBytes ;
4895
+
4896
+ if (![self usingCFStream ])
4897
+ {
4898
+ [self resumeWriteSource ];
4899
+ }
4900
+ }
4901
+
4902
+ if (bytesWritten > 0 )
4903
+ {
4904
+ // We're not done with the entire write, but we have written some bytes
4905
+
4906
+ if (delegateQueue && [delegate respondsToSelector: @selector (socket:didWritePartialDataOfLength:tag: )])
4907
+ {
4908
+ id theDelegate = delegate;
4909
+ GCDAsyncWritePacket *theWrite = currentWrite;
4870
4910
4871
- [pool drain ];
4872
- });
4911
+ dispatch_async (delegateQueue, ^{
4912
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc ] init ];
4913
+
4914
+ [theDelegate socket: self didWritePartialDataOfLength: bytesWritten tag: theWrite->tag];
4915
+
4916
+ [pool drain ];
4917
+ });
4918
+ }
4873
4919
}
4874
4920
}
4875
4921
@@ -4879,15 +4925,8 @@ - (void)doWriteData
4879
4925
{
4880
4926
[self closeWithError: [self errnoErrorWithReason: @" Error in write() function" ]];
4881
4927
}
4882
- else if (waiting)
4883
- {
4884
- if (![self usingCFStream ])
4885
- {
4886
- [self resumeWriteSource ];
4887
- }
4888
- }
4889
4928
4890
- // Do not add any code here without first adding return statements in the error cases above.
4929
+ // Do not add any code here without first adding a return statement in the error case above.
4891
4930
}
4892
4931
4893
4932
- (void )completeCurrentWrite
0 commit comments