singrdk/base/Applications/WebApps/FSUtils/WebAppFSUtils.sg

295 lines
12 KiB
Plaintext
Raw Normal View History

2008-03-05 09:52:00 -05:00
///////////////////////////////////////////////////////////////////////////////
//
// Microsoft Research Singularity
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// File: WebAppFSUtils.sg
//
// Note: File system operations for general use by webapps
//
using System;
using System.Collections;
using System.Diagnostics;
using Microsoft.SingSharp;
using Microsoft.SingSharp.Runtime;
using Microsoft.Singularity.Channels;
using Microsoft.Singularity.Directory;
using Microsoft.Singularity.WebApps;
using Microsoft.Singularity.WebApps.Contracts;
using System.Text;
using System.Web;
using FileSystem.Utils;
namespace Microsoft.Singularity.WebApps
{
public class WebAppFSUtils
{
private const string NoSuchPathPreamble =
"<html><head><title>No such path</title></head><body><h1 align=\"center\">No such path as \"";
private const string NoSuchPathPostamble = "\"</h1></body></html>";
private const string PathIsFilePreamble =
"<html><head><title>Path is file</title></head><body><h1 align=\"center\">Path \"";
private const string PathIsFilePostamble = "\" is a file</h1></body></html>";
private const string DirListingPreamble =
"<html><head><title>Directory Listing</title></head><body><h1>Directory Listing</h1>";
private const string DirListingPostamble = "</body></html>";
private const string NSListingPreamble =
"<html><head><title>Directory Listing</title></head><body><h1>Directory Listing</h1>";
private const string NSListingPostamble = "</body></html>";
private const string ErrorPreamble =
"<html><head><title>Error</title></head><body>";
private const string ErrorPostamble = "</body></html>";
public static void ServeFSPath(string! path, IHttpRequest! request,
DirectoryServiceContract.Imp:Ready! nsImp)
{
ulong startCycles, endCycles;
startCycles = Processor.CycleCount;
// First check if this is a file
FileContract.Imp:Ready fileChan = FileUtils.OpenFile(path, nsImp);
endCycles = Processor.CycleCount;
if ((endCycles - startCycles) > (ulong)((ulong)Processor.CyclesPerSecond / (ulong)5000)) {
//DebugStub.WriteLine("Slow file open");
}
if (fileChan != null) {
startCycles = Processor.CycleCount;
ServeFile(fileChan, request);
delete fileChan;
endCycles = Processor.CycleCount;
if ((endCycles - startCycles) > (ulong)((ulong)Processor.CyclesPerSecond / (ulong)5000)) {
//DebugStub.WriteLine("Slow file read");
}
return;
}
// Fallback: can this path be enumerated via the namespace?
if (IsValidNSPath(path, nsImp)) {
ServeNSListing(path, request, nsImp);
}
else {
// OK, we give up
request.SendStatus(404, "Not Found");
request.SendHeader("Content-type", "text/html");
request.SendBodyData(Encoding.ASCII.GetBytes(NoSuchPathPreamble));
request.SendBodyData(Encoding.ASCII.GetBytes(path));
request.SendBodyData(Encoding.ASCII.GetBytes(NoSuchPathPostamble));
request.Done();
}
}
public static void ServeFile(FileContract.Imp:Ready! fileImp, IHttpRequest! request)
{
request.SendStatus(200, "OK");
TransmitFileContents(fileImp, request);
request.Done();
}
public static void TransmitFileContents(FileContract.Imp:Ready! fileImp, IHttpRequest! request)
{
long readSize = 1024 * 64;
long readOffset = 0;
bool keepReading = true;
do {
byte[] in ExHeap buf = new[ExHeap] byte[readSize];
fileImp.SendRead(buf, 0, readOffset, readSize);
switch receive {
case fileImp.AckRead( _buf, long bytesRead, int error) :
if ((error != 0) || (_buf == null)) {
if (_buf != null)
{ delete _buf; }
keepReading = false;
}
else {
byte[]! in ExHeap original = _buf;
if (bytesRead > 0) {
if (bytesRead < readSize) {
// We must be at the end of the file; the FS only
// filled in part of our supplied block.
// Discard the remainder of the block.
byte[]! in ExHeap remainder = Bitter.SplitOff(ref original, (int)bytesRead);
delete remainder;
}
request.SendBodyData(original);
readOffset += bytesRead;
} else {
// By coincidence, our previous read took us to
// exactly the end of the file. We should
// optimize this away somehow
delete original;
}
if (bytesRead < readSize) {
// We're at the end
keepReading = false;
}
}
break;
case fileImp.ChannelClosed() :
keepReading = false;
break;
}
} while(keepReading);
}
public static void ServeNSListing(string! path, IHttpRequest! request,
DirectoryServiceContract.Imp:Ready! nsImp)
{
ArrayList! listing;
ErrorCode errorOut;
long length;
NodeType nodeType;
bool ok;
int rc = GetNSListing(path, nsImp, out listing);
if (rc != 0) {
request.SendStatus(500, "Error code " + rc + " while generating listing");
request.SendHeader("Content-type", "text/html");
request.SendBodyData(Encoding.ASCII.GetBytes(ErrorPreamble));
request.SendBodyData(Encoding.ASCII.GetBytes("Error code " + rc + " while generating listing"));
request.SendBodyData(Encoding.ASCII.GetBytes(ErrorPostamble));
request.Done();
}
else {
request.SendStatus(200, "OK");
request.SendHeader("Content-type", "text/html");
request.SendBodyData(Encoding.ASCII.GetBytes(NSListingPreamble));
request.SendBodyData(Encoding.ASCII.GetBytes("<b> Listing for \"" + path + "\":</b><p>"));
foreach(string! subPath in listing) {
string name;
if (path == "/") name = subPath;
else name = path + "/" +subPath;
ok = SdsUtils.GetAttributes(name, nsImp, out length, out nodeType, out errorOut);
if (!ok) {
// No hyperlink
request.SendBodyData(Encoding.ASCII.GetBytes(subPath + "(ERROR)<br/>"));
}
else {
if (nodeType == NodeType.File) {
// Hyperlink + file info
request.SendBodyData(Encoding.ASCII.GetBytes(
GetNodeTypeString(nodeType) +
"<a href=\"" + name + "\">" + subPath + "</a> " +
" (" + length + ")<br/>"));
}
else { // if (nodeType == NodeType.Directory) {
// Hyperlink + directory info
request.SendBodyData(Encoding.ASCII.GetBytes(
GetNodeTypeString(nodeType) +
"<a href=\"" + name + "\">[" + subPath + "]</a>"
+ "<br/>"));
}
}
}
request.SendBodyData(Encoding.ASCII.GetBytes("</p>" + NSListingPostamble));
request.Done();
}
}
private static string GetNodeTypeString(NodeType nodeType)
{
const string startVal = "(";
const string endVal = ")";
string retval = startVal;
switch (nodeType) {
case NodeType.File:
retval += "N";
break;
case NodeType.Directory:
retval += "D";
break;
case NodeType.SymLink:
retval += "L";
break;
case NodeType.ServiceProvider:
retval += "S";
break;
case NodeType.IoMemory:
retval += "I";
break;
case NodeType.BadNode:
retval += "BAD";
break;
}
if (retval.Equals(startVal)) {
return String.Empty;
}
else {
return retval + endVal;
}
}
private static bool IsValidNSPath(string! path, DirectoryServiceContract.Imp:Ready! nsImp)
{
NodeType nodeType;
long size;
ErrorCode error;
return FileUtils.GetAttributes(path, nsImp, out size, out nodeType, out error);
}
// Returns 0 on success; if return val != 0, outparam is null
private static int GetNSListing(string! path, DirectoryServiceContract.Imp:Ready! nsImp,
out ArrayList! listing)
{
listing = new ArrayList();
ErrorCode errorOut;
DirectoryServiceContract.Imp! dirImp;
DirectoryServiceContract.Exp! dirExp;
DirectoryServiceContract.NewChannel(out dirImp, out dirExp);
bool ok = SdsUtils.Bind(path, nsImp, dirExp, out errorOut);
if (!ok) {
delete dirImp;
return -1; //unable to bind to requested dir
}
dirImp.RecvSuccess();
EnumerationRecords[] in ExHeap responses =
SdsUtils.EnumerateDirectory (dirImp, out errorOut);
delete dirImp;
if (null == responses) {
return -1;
}
else {
for (int i=0; i < responses.Length; i++){
string name;
expose (responses[i]) {
name = Bitter.ToString2(responses[i].Path);
listing.Add(name);
}
Console.WriteLine(name);
}
delete responses;
return 0;
}
}
}
}