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); } }
// Returns the request offset into the builder buffer public UInt32 Build(ByteBuilder builder, HttpClient client, Boolean keepAlive) { if (method == null) { throw new InvalidOperationException("The HttpRequest method must be set"); } // // <METHOD> <resource> HTTP/1.1\r\n // builder.AppendAscii(method); // Todo: Should I verify that method is ascii beforehand? builder.AppendAscii(' '); if (!String.IsNullOrEmpty(resource)) { builder.AppendAscii(resource); // Todo: Should I verify that resource is ascii beforehand? } else if (resourceAppender != null) { resourceAppender(builder); } else { throw new InvalidOperationException("The HttpRequest resource must be set"); } builder.Append(VersionPart); // // Host: <host>[:<port>] // builder.Append(Http.HostHeaderPrefix); if (overrideHostHeader != null) { builder.AppendAscii(overrideHostHeader); } else { builder.AppendAscii(client.IPOrHost); if (client.Port != 80 || forcePortInHostHeader) { builder.AppendAscii(':'); builder.AppendNumber(client.Port, 10); } } builder.Append(Http.Newline); // // Header: Content-Length // // TODO: when do I not need a Content-Length? UInt32 contentLengthOffset = UInt32.MaxValue; { Boolean hasContent; UInt32 contentLength; if (content.contentAsBytes != null) { hasContent = true; contentLength = (UInt32)content.contentAsBytes.Length; } else if (content.contentAsString != null) { hasContent = true; contentLength = content.contentAsStringEncoder.GetEncodeLength(content.contentAsString); } else if (content.contentAppender != null) { hasContent = true; contentLength = UInt32.MaxValue; // Placeholder } else { hasContent = false; contentLength = 0; } if (hasContent) { builder.Append(Http.ContentLengthHeaderPrefix); contentLengthOffset = builder.contentLength; builder.AppendNumber(contentLength); builder.Append(Http.Newline); if (content.contentType != null) { builder.Append(Http.ContentTypeHeaderPrefix); builder.AppendAscii(content.contentType); builder.Append(Http.Newline); } } } // // Header: Connection // if (keepAlive) { builder.Append(Http.ConnectionHeaderPrefix); builder.Append(Http.ConnectionKeepAlive); builder.Append(Http.Newline); } else { builder.Append(Http.ConnectionHeaderPrefix); builder.Append(Http.ConnectionClose); builder.Append(Http.Newline); } // // Extra Headers // if (extraHeaders != null) { builder.Append(extraHeaders); } if (extraHeadersAppender != null) { extraHeadersAppender(builder); } // // End of Headers \r\n\r\n // builder.Append(Http.Newline); // // Content // if (content.contentAsBytes != null) { builder.Append(content.contentAsBytes); } else if (content.contentAsString != null) { builder.Append(content.contentAsStringEncoder, content.contentAsString); } else if (content.contentAppender != null) { // // Get the content // var contentStart = builder.contentLength; content.contentAppender(builder); UInt32 contentLength = builder.contentLength - contentStart; // Patch the request with the new content length UInt32 shift; // Shift everything before Content-Length: value to the right if (contentLength == 0) { builder.bytes[contentLengthOffset + 9] = (Byte)'0'; shift = 9; // Shift everything } else { shift = 9; var temp = contentLength; while (true) { builder.bytes[contentLengthOffset + shift] = (Byte)('0' + (temp % 10)); temp = temp / 10; if (temp == 0) { break; } shift--; } } // Shift the beginning of the request to compensate for a smaller Content-Length if (shift > 0) { var offset = contentLengthOffset - 1; while (true) { builder.bytes[offset + shift] = builder.bytes[offset]; if (offset == 0) { break; } offset--; } } return(shift); } return(0); }