Revision created by MOE tool push_codebase.
[gae.git] / java / src / main / com / google / appengine / api / socket / AppEngineSocketImpl.java
blobfa69dfd377598c23d2834612fd3444040e6c77f8
1 // Copyright 2012 Google Inc. All Rights Reserved.
3 package com.google.appengine.api.socket;
5 import com.google.appengine.api.socket.AppEngineSocketOptions.Option;
6 import com.google.appengine.api.socket.SocketServicePb.AcceptReply;
7 import com.google.appengine.api.socket.SocketServicePb.AcceptRequest;
8 import com.google.appengine.api.socket.SocketServicePb.AddressPort;
9 import com.google.appengine.api.socket.SocketServicePb.CloseReply;
10 import com.google.appengine.api.socket.SocketServicePb.CloseRequest;
11 import com.google.appengine.api.socket.SocketServicePb.ConnectReply;
12 import com.google.appengine.api.socket.SocketServicePb.ConnectRequest;
13 import com.google.appengine.api.socket.SocketServicePb.CreateSocketReply;
14 import com.google.appengine.api.socket.SocketServicePb.CreateSocketRequest;
15 import com.google.appengine.api.socket.SocketServicePb.CreateSocketRequest.SocketFamily;
16 import com.google.appengine.api.socket.SocketServicePb.CreateSocketRequest.SocketProtocol;
17 import com.google.appengine.api.socket.SocketServicePb.GetSocketNameReply;
18 import com.google.appengine.api.socket.SocketServicePb.GetSocketNameRequest;
19 import com.google.appengine.api.socket.SocketServicePb.GetSocketOptionsReply;
20 import com.google.appengine.api.socket.SocketServicePb.GetSocketOptionsRequest;
21 import com.google.appengine.api.socket.SocketServicePb.ListenReply;
22 import com.google.appengine.api.socket.SocketServicePb.ListenRequest;
23 import com.google.appengine.api.socket.SocketServicePb.ReceiveReply;
24 import com.google.appengine.api.socket.SocketServicePb.ReceiveRequest;
25 import com.google.appengine.api.socket.SocketServicePb.RemoteSocketServiceError;
26 import com.google.appengine.api.socket.SocketServicePb.RemoteSocketServiceError.ErrorCode;
27 import com.google.appengine.api.socket.SocketServicePb.RemoteSocketServiceError.SystemError;
28 import com.google.appengine.api.socket.SocketServicePb.SendReply;
29 import com.google.appengine.api.socket.SocketServicePb.SendRequest;
30 import com.google.appengine.api.socket.SocketServicePb.SetSocketOptionsReply;
31 import com.google.appengine.api.socket.SocketServicePb.SetSocketOptionsRequest;
32 import com.google.appengine.api.socket.SocketServicePb.ShutDownReply;
33 import com.google.appengine.api.socket.SocketServicePb.ShutDownRequest;
34 import com.google.appengine.api.socket.SocketServicePb.SocketOption.SocketOptionName;
35 import com.google.apphosting.api.ApiProxy;
37 import java.io.IOException;
38 import java.io.InputStream;
39 import java.io.OutputStream;
40 import java.io.Serializable;
41 import java.net.ConnectException;
42 import java.net.Inet4Address;
43 import java.net.Inet6Address;
44 import java.net.InetAddress;
45 import java.net.InetSocketAddress;
46 import java.net.SocketAddress;
47 import java.net.SocketException;
48 import java.net.SocketImpl;
49 import java.net.SocketOptions;
50 import java.net.SocketTimeoutException;
51 import java.net.UnknownHostException;
52 import java.util.Arrays;
53 import java.util.concurrent.atomic.AtomicLong;
55 /**
56 * Implements the {@link SocketImpl} interface for App Engine based sockets.
59 class AppEngineSocketImpl extends SocketImpl implements AppEngineSocketOptionsClient, Serializable {
60 private static final long serialVersionUID = -2683691443688405980L;
62 static enum SocketState {
63 /**
64 * Socket is uninitialized.
66 UNINITIALIZED,
68 /**
69 * {@link #create(boolean)} has been called.
70 * Note that this does not relate to remote socket create.
72 CREATE_CALLED,
74 /**
75 * Socket is bound.
77 BOUND,
79 /**
80 * Socket is connected.
82 CONNECTED,
84 /**
85 * Socket is listening.
87 LISTEN,
89 /**
90 * Socket is closed.
92 CLOSED
95 SocketState currentState = SocketState.UNINITIALIZED;
97 private SocketApiHelper socketHelper = null;
99 String descriptor = null;
101 SocketFamily socketFamily;
103 int soTimeout = -1;
105 private boolean readsShutdown = false;
106 private boolean writesShutdown = false;
108 private final AtomicLong sendOffset = new AtomicLong(0);
110 private AppEngineSocketInputStream socketInputStream = null;
113 * True indicates that this socket is for a (TCP) stream socket otherwise
114 * it is a datagram socket (UDP).
116 private boolean stream;
118 private InetAddress localAddress;
120 AppEngineSocketImpl() {
123 AppEngineSocketImpl(SocketApiHelper socketHelper) {
124 this.socketHelper = socketHelper;
127 SocketApiHelper getSocketApiHelper() {
128 return socketHelper == null ? AppEngineSocketImplFactory.SOCKET_API_HELPER : socketHelper;
131 protected InetAddress toInetAddress(AddressPort addressPort) throws UnknownHostException {
132 switch (socketFamily == null ? SocketFamily.IPv4 : socketFamily) {
133 case IPv4 : {
134 return Inet4Address.getByAddress(null, addressPort.getPackedAddressAsBytes());
136 case IPv6 : {
137 return Inet6Address.getByAddress(null, addressPort.getPackedAddressAsBytes(), null);
139 default: {
140 throw new ApiProxy.ApplicationException(
141 ErrorCode.INVALID_REQUEST.getValue(),
142 "Invalid socket family : " + socketFamily);
147 void setOption(
148 AppEngineSocketOptions.Option option, Object value) throws SocketException {
149 checkNotClosed();
150 option.validateAndApply(this, value);
154 * @see java.net.SocketOptions#setOption(int, java.lang.Object)
156 @Override
157 public void setOption(int opt, Object value) throws SocketException {
158 AppEngineSocketOptions.Option option = AppEngineSocketOptions.getOptionById(opt);
159 if (option == null) {
160 throw new SocketException("unrecognized socket option: " + opt);
162 setOption(option, value);
165 String statesToString(SocketState...socketStates) {
166 StringBuilder builder = new StringBuilder();
167 String prefix = "";
168 for (SocketState state : socketStates) {
169 builder.append(prefix);
170 prefix = ", ";
171 builder.append(state.toString());
173 return builder.toString();
176 void checkStateIsOneOf(String errorMsg, SocketState...socketStates) throws SocketException {
177 for (SocketState state : socketStates) {
178 if (currentState == state) {
179 return;
182 throw new SocketException(errorMsg + ": Expected to be in one of states : (" +
183 statesToString(socketStates) + "), was in state " + currentState);
186 void checkStateIsNotOneOf(String errorMsg, SocketState...socketStates) throws SocketException {
187 for (SocketState state : socketStates) {
188 if (currentState == state) {
189 throw new SocketException(errorMsg + ": Expected to not be in one of (" +
190 statesToString(socketStates) + "), was in state " + currentState);
196 * Checks that the socket is not closed.
198 private void checkNotClosed() throws SocketException {
199 checkStateIsNotOneOf("Socket is closed", SocketState.UNINITIALIZED, SocketState.CLOSED);
203 * Implements the remote service SetSocketOptions call.
205 @Override
206 public void setSocketOptionAsBytes(AppEngineSocketOptions.Option opt, byte[] value)
207 throws SocketException {
208 SocketOptionName name = opt.getOptName();
209 if (name == null) {
210 return;
212 checkStateIsNotOneOf("Socket is closed",
213 SocketState.UNINITIALIZED, SocketState.CREATE_CALLED, SocketState.CLOSED);
214 SetSocketOptionsRequest request = new SetSocketOptionsRequest();
215 SetSocketOptionsReply response = new SetSocketOptionsReply();
216 request.setSocketDescriptor(descriptor);
217 request.addOptions().setLevel(opt.getLevel()).setOption(name).setValueAsBytes(value);
219 getSocketApiHelper().makeSyncCall("SetSocketOptions", request, response, null);
222 @Override
223 public void setTimeout(int timeout) {
224 soTimeout = timeout;
228 * @see java.net.SocketOptions#getOption(int)
230 @Override
231 public Object getOption(int optID) throws SocketException {
232 if (SocketOptions.SO_BINDADDR == optID) {
233 checkNotClosed();
234 return localAddress;
236 if (SocketOptions.SO_TIMEOUT == optID) {
237 checkNotClosed();
238 return soTimeout <= 0 ? 0 : soTimeout;
241 AppEngineSocketOptions.Option option = AppEngineSocketOptions.getOptionById(optID);
242 if (option == null) {
243 throw new SocketException("unrecognized socket option: " + optID);
246 try {
247 return getOption(option);
248 } catch (SocketException e) {
249 if (SocketOptions.SO_LINGER == optID) {
250 return -1;
252 throw e;
257 * Returns the value of the given option.
258 * @throws SocketException
260 private Object getOption(Option option) throws SocketException {
261 return option.getOption(this);
265 * Called by {@link Option#getOption(AppEngineSocketImpl)}.
266 * @see AppEngineSocketOptionsClient#getSocketOptionAsBytes(Option)
268 @Override
269 public byte[] getSocketOptionAsBytes(Option option) throws SocketException {
270 GetSocketOptionsRequest request = new GetSocketOptionsRequest();
271 GetSocketOptionsReply response = new GetSocketOptionsReply();
272 request.setSocketDescriptor(descriptor);
273 request.addOptions().setLevel(option.getLevel()).setOption(option.getOptName());
274 getSocketApiHelper().makeSyncCall("GetSocketOptions", request, response, null);
275 return response.optionss().get(0).getValueAsBytes();
279 * @see java.net.SocketImpl#create(boolean)
281 @Override
282 protected synchronized void create(boolean stream) throws IOException {
283 checkStateIsOneOf("Socket is already created", SocketState.UNINITIALIZED);
284 currentState = SocketState.CREATE_CALLED;
285 this.stream = stream;
289 * @see java.net.SocketImpl#connect(java.lang.String, int)
291 @Override
292 protected synchronized void connect(String host, int port) throws IOException {
293 try {
294 InetAddress address = InetAddress.getByName(host);
295 connectToAddress(address, port, null);
296 } catch (UnknownHostException e) {
297 releaseSocket();
298 throw e;
303 * @see java.net.SocketImpl#connect(java.net.InetAddress, int)
305 @Override
306 protected synchronized void connect(InetAddress address, int port) throws IOException {
307 if (address == null) {
308 throw new IllegalArgumentException("null address is illegal for connect");
311 checkStateIsOneOf("socket is not created", SocketState.CREATE_CALLED, SocketState.BOUND);
313 connectToAddress(address, port, null);
317 * @see java.net.SocketImpl#connect(java.net.SocketAddress, int)
319 @Override
320 protected synchronized void connect(SocketAddress socketAddress, int timeout) throws IOException {
321 if (socketAddress == null) {
322 throw new IllegalArgumentException("null address is illegal for connect");
325 if (!(socketAddress instanceof InetSocketAddress)) {
326 throw new IllegalArgumentException("Address must be of type InetSocketAddress");
329 InetSocketAddress addr = (InetSocketAddress) socketAddress;
331 if (addr.isUnresolved()) {
332 throw new UnknownHostException(addr.getHostName());
335 connectToAddress(addr.getAddress(), addr.getPort(), timeout == 0 ? null : timeout);
338 private void connectToAddress(InetAddress address, int port, Integer timeoutMillis)
339 throws SocketException, SocketTimeoutException {
340 switch (currentState) {
341 case CREATE_CALLED: {
342 if (timeoutMillis == null) {
343 createSocket(null, 0, address, port, true);
344 } else {
345 createSocket(null, 0, address, port, false);
346 connectSocket(address, port, timeoutMillis);
348 break;
350 case BOUND: {
351 connectSocket(address, port, timeoutMillis);
352 break;
354 default: {
355 break;
359 this.port = port;
360 this.address = address;
363 private void processConnectError(RemoteSocketServiceError serviceError)
364 throws SocketException, SocketTimeoutException {
365 releaseSocket();
366 if (SystemError.valueOf(serviceError.getSystemError()) == SystemError.SYS_EINPROGRESS) {
367 throw new SocketTimeoutException();
369 if (SystemError.valueOf(serviceError.getSystemError()) == SystemError.SYS_ETIMEDOUT) {
370 throw new SocketTimeoutException();
372 if (SystemError.valueOf(serviceError.getSystemError()) == SystemError.SYS_ECONNREFUSED) {
373 throw new ConnectException(serviceError.getErrorDetail());
375 throw SocketApiHelper.translateError(
376 ErrorCode.SYSTEM_ERROR.getValue(),
377 "errno: " + serviceError.getSystemError() + ", detail:" + serviceError.getErrorDetail());
381 * Performs connect given a timeout.
383 private void connectSocket(InetAddress remoteAddress, int remotePort, Integer timeoutMillis)
384 throws SocketException, SocketTimeoutException {
385 if (remoteAddress == null) {
386 throw new IllegalArgumentException("remoteAddress must not be null if connect requested");
389 ConnectRequest request = new ConnectRequest().setSocketDescriptor(descriptor);
390 ConnectReply response = new ConnectReply();
392 request.getMutableRemoteIp().setPort(remotePort)
393 .setPackedAddressAsBytes(remoteAddress.getAddress());
394 if (timeoutMillis != null) {
395 request.setTimeoutSeconds(0.001D * timeoutMillis);
398 RemoteSocketServiceError serviceError = new RemoteSocketServiceError();
399 if (!getSocketApiHelper().makeSyncCall("Connect", request, response, serviceError)) {
400 processConnectError(serviceError);
403 currentState = SocketState.CONNECTED;
404 fixLocalAddress();
408 * Releases the socket and absorbs any exceptions.
410 private void releaseSocket() {
411 readsShutdown = false;
412 writesShutdown = false;
413 try {
414 close();
415 } catch (IOException ignored) {
417 currentState = SocketState.CLOSED;
420 private void fixLocalAddress() throws SocketException {
421 GetSocketNameRequest request = new GetSocketNameRequest().setSocketDescriptor(descriptor);
422 GetSocketNameReply response = new GetSocketNameReply();
424 getSocketApiHelper().makeSyncCall("GetSocketName", request, response, null);
425 AddressPort externalIp = response.getProxyExternalIp();
426 this.localport = externalIp.getPort();
428 try {
429 localAddress = toInetAddress(externalIp);
430 } catch (UnknownHostException e) {
431 throw new SocketException(e.toString());
436 * Create the remote socket with an optional bind and connect. Either the
437 * remoteAddress or bindAddress or both parameters must be provided.
438 * It is used to determine if socket is to be created with an IPV4 or IPV6
439 * address family.
440 * @throws SocketException
441 * @throws SocketTimeoutException
443 private void createSocket(
444 InetAddress bindAddress, int bindPort, InetAddress remoteAddress,
445 int remotePort, boolean connect)
446 throws SocketException, SocketTimeoutException {
448 CreateSocketRequest request = new CreateSocketRequest();
449 CreateSocketReply response = new CreateSocketReply();
451 if (bindAddress == null && remoteAddress == null) {
452 throw new IllegalArgumentException("Addresses must not be null.");
455 request.setProtocol(stream ? SocketProtocol.TCP : SocketProtocol.UDP);
457 if (bindAddress != null) {
458 if (bindAddress instanceof Inet6Address) {
459 socketFamily = SocketFamily.IPv6;
460 request.setFamily(socketFamily);
461 if (remoteAddress != null && !(remoteAddress instanceof Inet6Address)) {
462 throw new IllegalArgumentException(
463 "Cannot specify remote and local addresses of different family.");
465 } else if (bindAddress instanceof Inet4Address) {
466 socketFamily = SocketFamily.IPv4;
467 request.setFamily(socketFamily);
468 if (remoteAddress != null && !(remoteAddress instanceof Inet4Address)) {
469 throw new IllegalArgumentException(
470 "Cannot specify remote and local addresses of different family.");
472 } else {
473 throw new SocketException("Unknown InetAddress type: " + bindAddress.getClass().getName());
476 request.getMutableProxyExternalIp().setPort(bindPort)
477 .setPackedAddressAsBytes(bindAddress.getAddress());
478 } else {
479 if (remoteAddress instanceof Inet6Address) {
480 socketFamily = SocketFamily.IPv6;
481 request.setFamily(SocketFamily.IPv6);
482 } else if (remoteAddress instanceof Inet4Address) {
483 socketFamily = SocketFamily.IPv4;
484 request.setFamily(SocketFamily.IPv4);
485 } else {
486 throw new SocketException(
487 "Unknown InetAddress type: " + remoteAddress.getClass().getName());
491 if (connect) {
492 if (remoteAddress == null) {
493 throw new IllegalArgumentException("remoteAddress must not be null if connect requested");
495 request.getMutableRemoteIp().setPort(remotePort)
496 .setPackedAddressAsBytes(remoteAddress.getAddress());
497 RemoteSocketServiceError serviceError = new RemoteSocketServiceError();
498 if (!getSocketApiHelper().makeSyncCall("CreateSocket", request, response, serviceError)) {
499 processConnectError(serviceError);
501 } else {
502 getSocketApiHelper().makeSyncCall("CreateSocket", request, response, null);
505 descriptor = response.getSocketDescriptor();
506 currentState = connect ? SocketState.CONNECTED : SocketState.BOUND;
508 if (connect) {
509 fixLocalAddress();
514 * @see java.net.SocketImpl#bind(java.net.InetAddress, int)
516 @Override
517 protected synchronized void bind(InetAddress host, int port) throws IOException {
518 createSocket(host, port, null, 0, false);
519 currentState = SocketState.BOUND;
523 * @see java.net.SocketImpl#listen(int)
525 @Override
526 protected void listen(int backlog) throws IOException {
527 checkStateIsNotOneOf("Socket must be in a bound state.", SocketState.UNINITIALIZED,
528 SocketState.CREATE_CALLED, SocketState.CONNECTED, SocketState.CLOSED);
529 ListenRequest request = new ListenRequest().setSocketDescriptor(descriptor).setBacklog(backlog);
530 getSocketApiHelper().makeSyncCall("Listen", request, new ListenReply(), null);
531 currentState = SocketState.LISTEN;
535 * @see java.net.SocketImpl#accept(java.net.SocketImpl)
537 @Override
538 protected void accept(SocketImpl s) throws IOException {
539 checkStateIsOneOf("Socket is not in passive (accepting) mode.", SocketState.LISTEN);
540 if (!(s instanceof AppEngineSocketImpl)) {
541 throw new IllegalStateException(
542 "Expected a SocketImpl compatable with '" + this.getClass().getName() +
543 "'. A '" + s.getClass().getName() + "' was received.");
546 AppEngineSocketImpl acceptingSocket = (AppEngineSocketImpl) s;
547 AcceptReply response = new AcceptReply();
548 AcceptRequest request = new AcceptRequest().setSocketDescriptor(descriptor);
549 getSocketApiHelper().makeSyncCall("Accept", request, response, null);
550 acceptingSocket.doAccept(response);
554 * Initializes an AppEngineSocketImpl from an AcceptReply message.
556 private void doAccept(AcceptReply response) {
557 switch (currentState) {
558 case UNINITIALIZED:
559 case CREATE_CALLED:
560 case CLOSED: {
561 break;
563 default: {
564 releaseSocket();
568 stream = true;
569 descriptor = response.getNewSocketDescriptor();
570 AddressPort addressPort = response.getRemoteAddress();
571 try {
572 switch (addressPort.getPackedAddressAsBytes().length) {
573 case 4: {
574 socketFamily = SocketFamily.IPv4;
575 address = Inet4Address.getByAddress(null, addressPort.getPackedAddressAsBytes());
576 break;
578 case 16: {
579 socketFamily = SocketFamily.IPv6;
580 address = Inet6Address.getByAddress(null, addressPort.getPackedAddressAsBytes(), null);
581 break;
583 default : {
584 throw new IllegalStateException("Unexpected network address type.");
587 } catch (UnknownHostException e) {
588 throw new IllegalStateException(
589 "Unexpected UnknownHostException thrown unexpectedly: " + e.getMessage());
591 port = addressPort.getPort();
592 currentState = SocketState.CONNECTED;
596 * @see java.net.SocketImpl#getInputStream()
598 @Override
599 protected InputStream getInputStream() throws IOException {
600 if (currentState == SocketState.CLOSED) {
601 throw new IOException("Socket closed.");
604 if (readsShutdown) {
605 throw new IOException("Socket input is shutdown.");
608 if (socketInputStream == null) {
609 socketInputStream = new AppEngineSocketInputStream(this);
611 return socketInputStream;
615 * @see java.net.SocketImpl#getOutputStream()
617 @Override
618 protected OutputStream getOutputStream() throws IOException {
619 if (currentState == SocketState.CLOSED) {
620 throw new IOException("Socket closed.");
623 if (writesShutdown) {
624 throw new IOException("Socket output is shutdown.");
627 return new AppEngineSocketOutputStream(this);
631 * @see java.net.SocketImpl#available()
633 @Override
634 protected int available() throws IOException {
635 return 0;
639 * @see java.net.SocketImpl#close()
641 @Override
642 protected void close() throws IOException {
643 if (descriptor != null) {
644 try {
645 CloseRequest request = new CloseRequest().setSocketDescriptor(descriptor)
646 .setSendOffset(sendOffset.get());
647 getSocketApiHelper().makeSyncCall("Close", request, new CloseReply(), null);
648 } finally {
649 currentState = SocketState.CLOSED;
650 descriptor = null;
656 * @see java.net.SocketImpl#sendUrgentData(int)
658 @Override
659 protected void sendUrgentData(int data) throws IOException {
660 throw new IllegalStateException(
661 "AppEngineSocketImpl#sendUrgentData() function is unimplemented.");
665 * @see java.net.SocketImpl#shutdownInput
667 @Override
668 protected void shutdownInput() throws IOException {
669 if (currentState == SocketState.CONNECTED && !readsShutdown) {
670 ShutDownRequest request = new ShutDownRequest().setSocketDescriptor(descriptor)
671 .setSendOffset(sendOffset.get())
672 .setHow(ShutDownRequest.How.SOCKET_SHUT_RD);
673 getSocketApiHelper().makeSyncCall("ShutDown", request, new ShutDownReply(), null);
674 readsShutdown = true;
679 * @see java.net.SocketImpl#shutdownOutput
681 @Override
682 protected void shutdownOutput() throws IOException {
683 if (currentState == SocketState.CONNECTED && !writesShutdown) {
684 ShutDownRequest request = new ShutDownRequest().setSocketDescriptor(descriptor)
685 .setSendOffset(sendOffset.get())
686 .setHow(ShutDownRequest.How.SOCKET_SHUT_WR);
687 getSocketApiHelper().makeSyncCall("ShutDown", request, new ShutDownReply(), null);
688 writesShutdown = true;
693 * Used by {@link AppEngineSocketOutputStream} to send data.
695 * @param buf Buffer of bytes to write.
696 * @param off Offset into the buffer.
697 * @param len The length of bytes to write.
698 * @throws IOException On error conditions
700 protected void send(byte buf[], int off, int len) throws IOException {
701 checkStateIsNotOneOf("Socket is closed.",
702 SocketState.UNINITIALIZED, SocketState.CREATE_CALLED, SocketState.CLOSED);
703 if (writesShutdown) {
704 throw new IOException("Socket output is shutdown.");
707 byte copy[] = Arrays.copyOfRange(buf, off, off + len);
708 SendRequest request = new SendRequest().setSocketDescriptor(descriptor)
709 .setStreamOffset(sendOffset.getAndAdd(copy.length))
710 .setDataAsBytes(copy);
711 if (soTimeout > 0) {
712 request.setTimeoutSeconds(soTimeout * 0.001);
715 RemoteSocketServiceError serviceError = new RemoteSocketServiceError();
716 if (!getSocketApiHelper().makeSyncCall("Send", request, new SendReply(), serviceError)) {
717 if (serviceError.getSystemError() == SystemError.SYS_EAGAIN.getValue()) {
718 throw new SocketTimeoutException("Write timed out");
719 } else {
720 throw SocketApiHelper.translateError(
721 ErrorCode.SYSTEM_ERROR.getValue(),
722 "errno: " + serviceError.getSystemError() +
723 ", detail:" + serviceError.getErrorDetail());
729 * This is wrapped by {@link AppEngineSocketInputStream} and has the
730 * same semantics as {@link java.io.InputStream#read(byte[], int, int)}.
731 * @throws IOException On error conditions
733 protected int receive(byte[] buf, int off, int len) throws IOException {
734 checkStateIsNotOneOf("Socket is closed.",
735 SocketState.UNINITIALIZED, SocketState.CREATE_CALLED, SocketState.CLOSED);
737 if (readsShutdown) {
738 throw new IOException("Socket input is shutdown.");
741 if (len < 0 || off < 0 || buf.length - off < len) {
742 if (len == 0) {
743 return 0;
745 throw new ArrayIndexOutOfBoundsException();
748 ReceiveReply response = new ReceiveReply();
749 ReceiveRequest request = new ReceiveRequest().setSocketDescriptor(descriptor)
750 .setDataSize(len);
751 if (soTimeout > 0) {
752 request.setTimeoutSeconds(soTimeout * 0.001);
754 RemoteSocketServiceError serviceError = new RemoteSocketServiceError();
755 if (!getSocketApiHelper().makeSyncCall("Receive", request, response, serviceError)) {
756 if (serviceError.getSystemError() == SystemError.SYS_EAGAIN.getValue()) {
757 throw new SocketTimeoutException("Read timed out");
758 } else {
759 throw SocketApiHelper.translateError(
760 ErrorCode.SYSTEM_ERROR.getValue(),
761 "errno: " + serviceError.getSystemError() +
762 ", detail:" + serviceError.getErrorDetail());
766 byte readBytes[] = response.getDataAsBytes();
768 if (readBytes.length > 0) {
769 System.arraycopy(readBytes, 0, buf, off, readBytes.length);
770 } else {
771 return -1;
774 return readBytes.length;