예제 #1
0
        public void HeaderRecvHandler(ref SelectControl selectControl, Socket clientSocket, Buf ignore)
        {
            builder.EnsureTotalCapacity(builder.contentLength + 128);

            UInt32 dataOffset;

            try
            {
                dataOffset = Http.ReadHttpHeaders(clientSocket, builder);
            }
            catch (Exception e)
            {
                if (WebServer.Logger != null)
                {
                    WebServer.Logger.WriteLine("[{0}] Closed: {1}", clientLogString, e.Message);
                }
                selectControl.RemoveReceiveSocket(clientSocket);
                return;
            }

            //
            // Parse the request
            //
            try
            {
                UInt32 parseOffset = 0;

                Slice httpMethod;
                httpMethod.offset = parseOffset;
                httpMethod.limit  = builder.bytes.IndexOfUInt32(parseOffset, dataOffset, (Byte)' ');
                if (httpMethod.limit == UInt32.MaxValue)
                {
                    throw new FormatException("Invalid request: no space after HTTP method");
                }
                parseOffset = (uint)httpMethod.limit + 1;

                Slice httpResource;
                httpResource.offset = parseOffset;
                httpResource.limit  = builder.bytes.IndexOfUInt32(parseOffset, dataOffset, (Byte)' ');
                if (httpResource.limit == UInt32.MaxValue)
                {
                    throw new FormatException("Invalid request: no space after HTTP resource");
                }
                parseOffset = (uint)httpResource.limit + 1;

                this.method   = httpMethod.Decode(builder.bytes);
                this.resource = httpResource.Decode(builder.bytes);

                headerContentLength = Http.GetContentLength(builder.bytes, 0, dataOffset);

                if (headerContentLength != UInt32.MaxValue)
                {
                    throw new NotImplementedException(String.Format("Content-Length {0} is not implemented", headerContentLength));
                }
            }
            catch (Exception e)
            {
                if (WebServer.Logger != null)
                {
                    WebServer.Logger.WriteLine("[{0}] InvalidRequest: {1}", clientLogString, e.Message);
                }
                clientSocket.Shutdown(SocketShutdown.Both);
                clientSocket.Close();
                selectControl.RemoveReceiveSocket(clientSocket);
                return;
            }

            if (WebServer.Logger != null)
            {
                WebServer.Logger.WriteLine("[{0}] {1} {2}", clientLogString, method, resource);
            }

            if (!method.Equals("GET"))
            {
                if (WebServer.Logger != null)
                {
                    WebServer.Logger.WriteLine("[{0}] Unsupported HTTP Method: {1}", clientLogString, method);
                }
                clientSocket.Shutdown(SocketShutdown.Both);
                clientSocket.Close();
                selectControl.RemoveReceiveSocket(clientSocket);
                return;
            }
            String filename = WebServer.HttpResourceToFile(resource);

            if (!File.Exists(filename))
            {
                clientSocket.Send(NotFound404);
                clientSocket.Shutdown(SocketShutdown.Both);
                clientSocket.Close();
                selectControl.RemoveReceiveSocket(clientSocket);
                return;
            }


            builder.Clear();
            FileInfo fileInfo   = new FileInfo(filename);
            Int64    fileLength = fileInfo.Length;

            builder.AppendAscii("HTTP/1.1 200 OK\r\nContent-Length: ");
            builder.AppendAscii(fileLength.ToString());
            builder.AppendAscii("\r\n\r\n");
            using (FileStream fileStream = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
            {
                UInt32 bufferLeft = (uint)builder.bytes.Length - builder.contentLength;

                if (fileLength <= bufferLeft)
                {
                    fileStream.ReadFullSize(builder.bytes, (int)builder.contentLength, (int)fileLength);
                    clientSocket.Send(builder.bytes, 0, (int)(builder.contentLength + fileLength), 0);
                }
                else
                {
                    Int64 fileLeft = fileLength;

                    fileStream.ReadFullSize(builder.bytes, (int)builder.contentLength, (int)bufferLeft);
                    clientSocket.Send(builder.bytes);
                    fileLeft -= bufferLeft;

                    while (fileLeft > builder.bytes.Length)
                    {
                        fileStream.ReadFullSize(builder.bytes, 0, builder.bytes.Length);
                        clientSocket.Send(builder.bytes);
                        fileLeft -= builder.bytes.Length;
                    }

                    if (fileLeft > 0)
                    {
                        fileStream.ReadFullSize(builder.bytes, 0, (int)fileLeft);
                        clientSocket.Send(builder.bytes, 0, (int)fileLeft, 0);
                    }
                }

                clientSocket.Close();
                selectControl.RemoveReceiveSocket(clientSocket);
            }
        }
예제 #2
0
        // Returns offset into response content
        UInt32 ReadResponse(Boolean keepAlive, ByteBuilder builder)
        {
            UInt32 contentOffset;

            while (true)
            {
                Int32 bytesReceived = socket.Receive(builder.bytes, (int)builder.contentLength,
                                                     (int)(builder.bytes.Length - builder.contentLength), 0);
                if (bytesReceived <= 0)
                {
                    throw new FormatException(String.Format("Socket.Receive returned {0} while reading response. Response so far is '{1}'",
                                                            bytesReceived, Encoding.UTF8.GetString(builder.bytes, 0, (int)builder.contentLength)));
                }

                UInt32 checkOffset = builder.contentLength;
                builder.contentLength += (UInt32)bytesReceived;

                // Search for the ending \r\n\r\n
                Byte[] checkBuffer = builder.bytes;
                if (checkOffset >= 3)
                {
                    checkOffset -= 3; // Reverse 3 chars in case the last request had the partial end of double newline
                }
                for (; checkOffset + 3 < builder.contentLength; checkOffset++)
                {
                    if (checkBuffer[checkOffset] == (Byte)'\r' &&
                        checkBuffer[checkOffset + 1] == (Byte)'\n' &&
                        checkBuffer[checkOffset + 2] == (Byte)'\r' &&
                        checkBuffer[checkOffset + 3] == (Byte)'\n')
                    {
                        contentOffset = checkOffset + 4;
                        goto DONE_WITH_HEADERS;
                    }
                }

                // Expand the receive buffer if necessary
                if (builder.bytes.Length - builder.contentLength < MinimumReceiveBuffer)
                {
                    builder.EnsureTotalCapacity(builder.contentLength + MinimumExpandReceiveBuffer);
                }
            }
DONE_WITH_HEADERS:

            //
            // Get Content Length
            //
            // TODO: verify this works
            UInt32 contentLengthFromHeader = Http.GetContentLength(builder.bytes, 0, builder.contentLength);


            //
            // TODO: Determine Message Length properly
            // 1. If the response cannot include a message body then it MUST NOT have any content (such
            //    as 1xx, 204, 304, or any HEAD response)
            // 2. If a Transfer-Encoding header field is present with any other value besides "identity", then
            //    the transfer-length is defined by use of the "chunked" transfer-coding unless the message
            //    is terminated by closing the connection
            // 3. Use the Content-Length header
            // 4. Using Multipart/byteranges....???
            // 5. Server closes the connection


            //
            // Read the rest of the response
            //
            if (contentLengthFromHeader == UInt32.MaxValue)
            {
                if (keepAlive)
                {
                    // TODO: check the response code
                    // The server did not include a Content-Length HTTP header
                    // This is problematic if the socket connection is Keep-Alive
                    //     Note: I am currently assuming keepAlive from the client request...I should check the server header
                    //           for CloseConnection
                    throw new FormatException("The server did not include a Content-Length HTTP header but the socket connection is Keep-Alive");
                }
                else
                {
                    while (true)
                    {
                        Int32 bytesReceived = socket.Receive(builder.bytes, (int)builder.contentLength,
                                                             (int)(builder.bytes.Length - builder.contentLength), 0);
                        if (bytesReceived <= 0)
                        {
                            throw new FormatException(String.Format("Socket.Receive returned {0} while reading response. Response so far is '{1}'",
                                                                    bytesReceived, Encoding.UTF8.GetString(builder.bytes, 0, (int)builder.contentLength)));
                        }

                        builder.contentLength += (UInt32)bytesReceived;

                        // Expand the receive buffer if necessary
                        if (builder.bytes.Length - builder.contentLength < MinimumReceiveBuffer)
                        {
                            builder.EnsureTotalCapacity(builder.contentLength + MinimumExpandReceiveBuffer);
                        }
                    }
                }
            }
            else if (contentLengthFromHeader > 0)
            {
                UInt32 contentSoFar = builder.contentLength - contentOffset;
                Int32  contentLeft  = (Int32)(contentLengthFromHeader - contentSoFar);
                if (contentLeft > 0)
                {
                    builder.EnsureTotalCapacity(builder.contentLength + (UInt32)contentLeft);
                    do
                    {
                        Int32 bytesReceived = socket.Receive(builder.bytes, (int)builder.contentLength,
                                                             (int)(builder.bytes.Length - builder.contentLength), 0);
                        if (bytesReceived <= 0)
                        {
                            throw new FormatException(String.Format("Socket.Receive returned {0} while reading response. Response so far is '{1}'",
                                                                    bytesReceived, Encoding.UTF8.GetString(builder.bytes, 0, (int)builder.contentLength)));
                        }

                        builder.contentLength += (UInt32)bytesReceived;

                        // Expand the receive buffer if necessary
                        if (builder.bytes.Length - builder.contentLength < MinimumReceiveBuffer)
                        {
                            builder.EnsureTotalCapacity(builder.contentLength + MinimumExpandReceiveBuffer);
                        }
                        contentLeft -= bytesReceived;
                    } while (contentLeft > 0);
                }
            }

            if (!keepAlive)
            {
                socket.ShutdownAndDispose();
                socket = null;
            }

            return(contentOffset);
        }