diff --git a/org.eclipse.jgit.http.server/.classpath b/org.eclipse.jgit.http.server/.classpath
new file mode 100644
index 0000000000000000000000000000000000000000..304e86186aa0aecc956652a558f33921111ab213
--- /dev/null
+++ b/org.eclipse.jgit.http.server/.classpath
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="src" path="src"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.5"/>
+	<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+	<classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/org.eclipse.jgit.http.server/.fbprefs b/org.eclipse.jgit.http.server/.fbprefs
new file mode 100644
index 0000000000000000000000000000000000000000..81a0767ff644f3779802617bb81e87e7d737c768
--- /dev/null
+++ b/org.eclipse.jgit.http.server/.fbprefs
@@ -0,0 +1,125 @@
+#FindBugs User Preferences
+#Mon May 04 16:24:13 PDT 2009
+detectorAppendingToAnObjectOutputStream=AppendingToAnObjectOutputStream|true
+detectorBadAppletConstructor=BadAppletConstructor|false
+detectorBadResultSetAccess=BadResultSetAccess|true
+detectorBadSyntaxForRegularExpression=BadSyntaxForRegularExpression|true
+detectorBadUseOfReturnValue=BadUseOfReturnValue|true
+detectorBadlyOverriddenAdapter=BadlyOverriddenAdapter|true
+detectorBooleanReturnNull=BooleanReturnNull|true
+detectorCallToUnsupportedMethod=CallToUnsupportedMethod|true
+detectorCheckImmutableAnnotation=CheckImmutableAnnotation|true
+detectorCheckTypeQualifiers=CheckTypeQualifiers|true
+detectorCloneIdiom=CloneIdiom|false
+detectorComparatorIdiom=ComparatorIdiom|true
+detectorConfusedInheritance=ConfusedInheritance|true
+detectorConfusionBetweenInheritedAndOuterMethod=ConfusionBetweenInheritedAndOuterMethod|true
+detectorCrossSiteScripting=CrossSiteScripting|true
+detectorDoInsideDoPrivileged=DoInsideDoPrivileged|true
+detectorDontCatchIllegalMonitorStateException=DontCatchIllegalMonitorStateException|true
+detectorDontUseEnum=DontUseEnum|true
+detectorDroppedException=DroppedException|true
+detectorDumbMethodInvocations=DumbMethodInvocations|true
+detectorDumbMethods=DumbMethods|true
+detectorDuplicateBranches=DuplicateBranches|true
+detectorEmptyZipFileEntry=EmptyZipFileEntry|true
+detectorEqualsOperandShouldHaveClassCompatibleWithThis=EqualsOperandShouldHaveClassCompatibleWithThis|true
+detectorFinalizerNullsFields=FinalizerNullsFields|true
+detectorFindBadCast2=FindBadCast2|true
+detectorFindBadForLoop=FindBadForLoop|true
+detectorFindCircularDependencies=FindCircularDependencies|false
+detectorFindDeadLocalStores=FindDeadLocalStores|true
+detectorFindDoubleCheck=FindDoubleCheck|true
+detectorFindEmptySynchronizedBlock=FindEmptySynchronizedBlock|true
+detectorFindFieldSelfAssignment=FindFieldSelfAssignment|true
+detectorFindFinalizeInvocations=FindFinalizeInvocations|true
+detectorFindFloatEquality=FindFloatEquality|true
+detectorFindHEmismatch=FindHEmismatch|true
+detectorFindInconsistentSync2=FindInconsistentSync2|true
+detectorFindJSR166LockMonitorenter=FindJSR166LockMonitorenter|true
+detectorFindLocalSelfAssignment2=FindLocalSelfAssignment2|true
+detectorFindMaskedFields=FindMaskedFields|true
+detectorFindMismatchedWaitOrNotify=FindMismatchedWaitOrNotify|true
+detectorFindNakedNotify=FindNakedNotify|true
+detectorFindNonSerializableStoreIntoSession=FindNonSerializableStoreIntoSession|true
+detectorFindNonSerializableValuePassedToWriteObject=FindNonSerializableValuePassedToWriteObject|true
+detectorFindNonShortCircuit=FindNonShortCircuit|true
+detectorFindNullDeref=FindNullDeref|true
+detectorFindNullDerefsInvolvingNonShortCircuitEvaluation=FindNullDerefsInvolvingNonShortCircuitEvaluation|true
+detectorFindOpenStream=FindOpenStream|true
+detectorFindPuzzlers=FindPuzzlers|true
+detectorFindRefComparison=FindRefComparison|true
+detectorFindReturnRef=FindReturnRef|true
+detectorFindRunInvocations=FindRunInvocations|true
+detectorFindSelfComparison=FindSelfComparison|true
+detectorFindSelfComparison2=FindSelfComparison2|true
+detectorFindSleepWithLockHeld=FindSleepWithLockHeld|true
+detectorFindSpinLoop=FindSpinLoop|true
+detectorFindSqlInjection=FindSqlInjection|true
+detectorFindTwoLockWait=FindTwoLockWait|true
+detectorFindUncalledPrivateMethods=FindUncalledPrivateMethods|true
+detectorFindUnconditionalWait=FindUnconditionalWait|true
+detectorFindUninitializedGet=FindUninitializedGet|true
+detectorFindUnrelatedTypesInGenericContainer=FindUnrelatedTypesInGenericContainer|true
+detectorFindUnreleasedLock=FindUnreleasedLock|true
+detectorFindUnsatisfiedObligation=FindUnsatisfiedObligation|true
+detectorFindUnsyncGet=FindUnsyncGet|true
+detectorFindUselessControlFlow=FindUselessControlFlow|true
+detectorFormatStringChecker=FormatStringChecker|true
+detectorHugeSharedStringConstants=HugeSharedStringConstants|true
+detectorIDivResultCastToDouble=IDivResultCastToDouble|true
+detectorIncompatMask=IncompatMask|true
+detectorInconsistentAnnotations=InconsistentAnnotations|true
+detectorInefficientMemberAccess=InefficientMemberAccess|false
+detectorInefficientToArray=InefficientToArray|true
+detectorInfiniteLoop=InfiniteLoop|true
+detectorInfiniteRecursiveLoop=InfiniteRecursiveLoop|true
+detectorInfiniteRecursiveLoop2=InfiniteRecursiveLoop2|false
+detectorInheritanceUnsafeGetResource=InheritanceUnsafeGetResource|true
+detectorInitializationChain=InitializationChain|true
+detectorInstantiateStaticClass=InstantiateStaticClass|true
+detectorInvalidJUnitTest=InvalidJUnitTest|true
+detectorIteratorIdioms=IteratorIdioms|true
+detectorLazyInit=LazyInit|true
+detectorLoadOfKnownNullValue=LoadOfKnownNullValue|true
+detectorMethodReturnCheck=MethodReturnCheck|true
+detectorMultithreadedInstanceAccess=MultithreadedInstanceAccess|true
+detectorMutableLock=MutableLock|true
+detectorMutableStaticFields=MutableStaticFields|true
+detectorNaming=Naming|true
+detectorNumberConstructor=NumberConstructor|true
+detectorOverridingEqualsNotSymmetrical=OverridingEqualsNotSymmetrical|true
+detectorPreferZeroLengthArrays=PreferZeroLengthArrays|true
+detectorPublicSemaphores=PublicSemaphores|false
+detectorQuestionableBooleanAssignment=QuestionableBooleanAssignment|true
+detectorReadReturnShouldBeChecked=ReadReturnShouldBeChecked|true
+detectorRedundantInterfaces=RedundantInterfaces|true
+detectorRepeatedConditionals=RepeatedConditionals|true
+detectorRuntimeExceptionCapture=RuntimeExceptionCapture|true
+detectorSerializableIdiom=SerializableIdiom|true
+detectorStartInConstructor=StartInConstructor|true
+detectorStaticCalendarDetector=StaticCalendarDetector|true
+detectorStringConcatenation=StringConcatenation|true
+detectorSuperfluousInstanceOf=SuperfluousInstanceOf|true
+detectorSuspiciousThreadInterrupted=SuspiciousThreadInterrupted|true
+detectorSwitchFallthrough=SwitchFallthrough|true
+detectorSynchronizeAndNullCheckField=SynchronizeAndNullCheckField|true
+detectorSynchronizeOnClassLiteralNotGetClass=SynchronizeOnClassLiteralNotGetClass|true
+detectorSynchronizingOnContentsOfFieldToProtectField=SynchronizingOnContentsOfFieldToProtectField|true
+detectorURLProblems=URLProblems|true
+detectorUncallableMethodOfAnonymousClass=UncallableMethodOfAnonymousClass|true
+detectorUnnecessaryMath=UnnecessaryMath|true
+detectorUnreadFields=UnreadFields|true
+detectorUseObjectEquals=UseObjectEquals|false
+detectorUselessSubclassMethod=UselessSubclassMethod|false
+detectorVarArgsProblems=VarArgsProblems|true
+detectorVolatileUsage=VolatileUsage|true
+detectorWaitInLoop=WaitInLoop|true
+detectorWrongMapIterator=WrongMapIterator|true
+detectorXMLFactoryBypass=XMLFactoryBypass|true
+detector_threshold=2
+effort=default
+excludefilter0=findBugs/FindBugsExcludeFilter.xml
+filter_settings=Medium|BAD_PRACTICE,CORRECTNESS,MT_CORRECTNESS,PERFORMANCE,STYLE|false
+filter_settings_neg=MALICIOUS_CODE,NOISE,I18N,SECURITY,EXPERIMENTAL|
+run_at_full_build=true
diff --git a/org.eclipse.jgit.http.server/.gitignore b/org.eclipse.jgit.http.server/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..934e0e06ffa0a2aeedbe1341d321549336719cc0
--- /dev/null
+++ b/org.eclipse.jgit.http.server/.gitignore
@@ -0,0 +1,2 @@
+/bin
+/target
diff --git a/org.eclipse.jgit.http.server/.project b/org.eclipse.jgit.http.server/.project
new file mode 100644
index 0000000000000000000000000000000000000000..24bd8442e9f22d484c6a45cd96934238df1aad5a
--- /dev/null
+++ b/org.eclipse.jgit.http.server/.project
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>org.eclipse.jgit.http.server</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.pde.ManifestBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.pde.SchemaBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+		<nature>org.eclipse.pde.PluginNature</nature>
+	</natures>
+</projectDescription>
diff --git a/org.eclipse.jgit.http.server/.settings/org.eclipse.core.resources.prefs b/org.eclipse.jgit.http.server/.settings/org.eclipse.core.resources.prefs
new file mode 100644
index 0000000000000000000000000000000000000000..66ac15c47ce72abbc125a7109abb832f413af75a
--- /dev/null
+++ b/org.eclipse.jgit.http.server/.settings/org.eclipse.core.resources.prefs
@@ -0,0 +1,3 @@
+#Mon Aug 11 16:46:12 PDT 2008
+eclipse.preferences.version=1
+encoding/<project>=UTF-8
diff --git a/org.eclipse.jgit.http.server/.settings/org.eclipse.core.runtime.prefs b/org.eclipse.jgit.http.server/.settings/org.eclipse.core.runtime.prefs
new file mode 100644
index 0000000000000000000000000000000000000000..006e07ede5d7ee605129ae23da9c20b9bd956341
--- /dev/null
+++ b/org.eclipse.jgit.http.server/.settings/org.eclipse.core.runtime.prefs
@@ -0,0 +1,3 @@
+#Mon Mar 24 18:55:50 EDT 2008
+eclipse.preferences.version=1
+line.separator=\n
diff --git a/org.eclipse.jgit.http.server/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit.http.server/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000000000000000000000000000000000000..76557139ecfb00e96d6a60f7cd0ed9d35dc85bf1
--- /dev/null
+++ b/org.eclipse.jgit.http.server/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,334 @@
+#Fri Oct 02 18:43:47 PDT 2009
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5
+org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
+org.eclipse.jdt.core.compiler.compliance=1.5
+org.eclipse.jdt.core.compiler.debug.lineNumber=generate
+org.eclipse.jdt.core.compiler.debug.localVariable=generate
+org.eclipse.jdt.core.compiler.debug.sourceFile=generate
+org.eclipse.jdt.core.compiler.doc.comment.support=enabled
+org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.autoboxing=warning
+org.eclipse.jdt.core.compiler.problem.deprecation=warning
+org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled
+org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled
+org.eclipse.jdt.core.compiler.problem.discouragedReference=warning
+org.eclipse.jdt.core.compiler.problem.emptyStatement=warning
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.problem.fallthroughCase=warning
+org.eclipse.jdt.core.compiler.problem.fieldHiding=warning
+org.eclipse.jdt.core.compiler.problem.finalParameterBound=warning
+org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=error
+org.eclipse.jdt.core.compiler.problem.forbiddenReference=error
+org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=error
+org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning
+org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=warning
+org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=error
+org.eclipse.jdt.core.compiler.problem.invalidJavadoc=error
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTags=enabled
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsDeprecatedRef=enabled
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsNotVisibleRef=enabled
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsVisibility=private
+org.eclipse.jdt.core.compiler.problem.localVariableHiding=warning
+org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=error
+org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=ignore
+org.eclipse.jdt.core.compiler.problem.missingJavadocComments=error
+org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsOverriding=disabled
+org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsVisibility=protected
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagDescription=return_tag
+org.eclipse.jdt.core.compiler.problem.missingJavadocTags=error
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagsOverriding=disabled
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagsVisibility=private
+org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=ignore
+org.eclipse.jdt.core.compiler.problem.missingSerialVersion=warning
+org.eclipse.jdt.core.compiler.problem.noEffectAssignment=error
+org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=error
+org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=ignore
+org.eclipse.jdt.core.compiler.problem.nullReference=warning
+org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning
+org.eclipse.jdt.core.compiler.problem.parameterAssignment=ignore
+org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=error
+org.eclipse.jdt.core.compiler.problem.potentialNullReference=warning
+org.eclipse.jdt.core.compiler.problem.rawTypeReference=ignore
+org.eclipse.jdt.core.compiler.problem.redundantNullCheck=warning
+org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled
+org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=error
+org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled
+org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning
+org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning
+org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=warning
+org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning
+org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=error
+org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=warning
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=disabled
+org.eclipse.jdt.core.compiler.problem.unusedImport=error
+org.eclipse.jdt.core.compiler.problem.unusedLabel=error
+org.eclipse.jdt.core.compiler.problem.unusedLocal=error
+org.eclipse.jdt.core.compiler.problem.unusedParameter=warning
+org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled
+org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled
+org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=error
+org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=error
+org.eclipse.jdt.core.compiler.source=1.5
+org.eclipse.jdt.core.formatter.align_type_members_on_columns=false
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_assignment=0
+org.eclipse.jdt.core.formatter.alignment_for_binary_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_compact_if=16
+org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=80
+org.eclipse.jdt.core.formatter.alignment_for_enum_constants=0
+org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16
+org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16
+org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=16
+org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16
+org.eclipse.jdt.core.formatter.blank_lines_after_imports=1
+org.eclipse.jdt.core.formatter.blank_lines_after_package=1
+org.eclipse.jdt.core.formatter.blank_lines_before_field=1
+org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=0
+org.eclipse.jdt.core.formatter.blank_lines_before_imports=1
+org.eclipse.jdt.core.formatter.blank_lines_before_member_type=1
+org.eclipse.jdt.core.formatter.blank_lines_before_method=1
+org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=1
+org.eclipse.jdt.core.formatter.blank_lines_before_package=0
+org.eclipse.jdt.core.formatter.blank_lines_between_import_groups=1
+org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=1
+org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_array_initializer=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_block=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_block_in_case=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_enum_constant=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_switch=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines=false
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=false
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=false
+org.eclipse.jdt.core.formatter.comment.format_block_comments=true
+org.eclipse.jdt.core.formatter.comment.format_comments=true
+org.eclipse.jdt.core.formatter.comment.format_header=false
+org.eclipse.jdt.core.formatter.comment.format_html=true
+org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=true
+org.eclipse.jdt.core.formatter.comment.format_line_comments=true
+org.eclipse.jdt.core.formatter.comment.format_source_code=true
+org.eclipse.jdt.core.formatter.comment.indent_parameter_description=true
+org.eclipse.jdt.core.formatter.comment.indent_root_tags=true
+org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=insert
+org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=insert
+org.eclipse.jdt.core.formatter.comment.line_length=80
+org.eclipse.jdt.core.formatter.compact_else_if=true
+org.eclipse.jdt.core.formatter.continuation_indentation=2
+org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=2
+org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true
+org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true
+org.eclipse.jdt.core.formatter.indent_empty_lines=false
+org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true
+org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true
+org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true
+org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=false
+org.eclipse.jdt.core.formatter.indentation.size=4
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_member=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_binary_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_after_unary_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_binary_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=insert
+org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert
+org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw=insert
+org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false
+org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false
+org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=false
+org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false
+org.eclipse.jdt.core.formatter.lineSplit=80
+org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=false
+org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false
+org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0
+org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=1
+org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=true
+org.eclipse.jdt.core.formatter.tabulation.char=tab
+org.eclipse.jdt.core.formatter.tabulation.size=4
+org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false
+org.eclipse.jdt.core.formatter.wrap_before_binary_operator=true
diff --git a/org.eclipse.jgit.http.server/.settings/org.eclipse.jdt.ui.prefs b/org.eclipse.jgit.http.server/.settings/org.eclipse.jdt.ui.prefs
new file mode 100644
index 0000000000000000000000000000000000000000..02fa3390bec8953d5d2fa854ea07f2d6b3b169eb
--- /dev/null
+++ b/org.eclipse.jgit.http.server/.settings/org.eclipse.jdt.ui.prefs
@@ -0,0 +1,61 @@
+#Wed Jan 06 12:13:14 PST 2010
+eclipse.preferences.version=1
+editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true
+formatter_profile=_JGit Format
+formatter_settings_version=11
+org.eclipse.jdt.ui.ignorelowercasenames=true
+org.eclipse.jdt.ui.importorder=java;javax;org;com;
+org.eclipse.jdt.ui.ondemandthreshold=99
+org.eclipse.jdt.ui.staticondemandthreshold=99
+org.eclipse.jdt.ui.text.custom_code_templates=<?xml version\="1.0" encoding\="UTF-8" standalone\="no"?><templates/>
+sp_cleanup.add_default_serial_version_id=true
+sp_cleanup.add_generated_serial_version_id=false
+sp_cleanup.add_missing_annotations=false
+sp_cleanup.add_missing_deprecated_annotations=true
+sp_cleanup.add_missing_methods=false
+sp_cleanup.add_missing_nls_tags=false
+sp_cleanup.add_missing_override_annotations=true
+sp_cleanup.add_serial_version_id=false
+sp_cleanup.always_use_blocks=true
+sp_cleanup.always_use_parentheses_in_expressions=false
+sp_cleanup.always_use_this_for_non_static_field_access=false
+sp_cleanup.always_use_this_for_non_static_method_access=false
+sp_cleanup.convert_to_enhanced_for_loop=false
+sp_cleanup.correct_indentation=false
+sp_cleanup.format_source_code=false
+sp_cleanup.format_source_code_changes_only=false
+sp_cleanup.make_local_variable_final=false
+sp_cleanup.make_parameters_final=false
+sp_cleanup.make_private_fields_final=true
+sp_cleanup.make_type_abstract_if_missing_method=false
+sp_cleanup.make_variable_declarations_final=false
+sp_cleanup.never_use_blocks=false
+sp_cleanup.never_use_parentheses_in_expressions=true
+sp_cleanup.on_save_use_additional_actions=true
+sp_cleanup.organize_imports=false
+sp_cleanup.qualify_static_field_accesses_with_declaring_class=false
+sp_cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true
+sp_cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true
+sp_cleanup.qualify_static_member_accesses_with_declaring_class=false
+sp_cleanup.qualify_static_method_accesses_with_declaring_class=false
+sp_cleanup.remove_private_constructors=true
+sp_cleanup.remove_trailing_whitespaces=true
+sp_cleanup.remove_trailing_whitespaces_all=true
+sp_cleanup.remove_trailing_whitespaces_ignore_empty=false
+sp_cleanup.remove_unnecessary_casts=false
+sp_cleanup.remove_unnecessary_nls_tags=false
+sp_cleanup.remove_unused_imports=false
+sp_cleanup.remove_unused_local_variables=false
+sp_cleanup.remove_unused_private_fields=true
+sp_cleanup.remove_unused_private_members=false
+sp_cleanup.remove_unused_private_methods=true
+sp_cleanup.remove_unused_private_types=true
+sp_cleanup.sort_members=false
+sp_cleanup.sort_members_all=false
+sp_cleanup.use_blocks=false
+sp_cleanup.use_blocks_only_for_return_and_throw=false
+sp_cleanup.use_parentheses_in_expressions=false
+sp_cleanup.use_this_for_non_static_field_access=false
+sp_cleanup.use_this_for_non_static_field_access_only_if_necessary=true
+sp_cleanup.use_this_for_non_static_method_access=false
+sp_cleanup.use_this_for_non_static_method_access_only_if_necessary=true
diff --git a/org.eclipse.jgit.http.server/META-INF/MANIFEST.MF b/org.eclipse.jgit.http.server/META-INF/MANIFEST.MF
new file mode 100644
index 0000000000000000000000000000000000000000..92b02e87654d4aaa6a3819c238f1cb0194afa626
--- /dev/null
+++ b/org.eclipse.jgit.http.server/META-INF/MANIFEST.MF
@@ -0,0 +1,21 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: %plugin_name
+Bundle-SymbolicName: org.eclipse.jgit.http.server
+Bundle-Version: 0.6.0.qualifier
+Bundle-Localization: plugin
+Bundle-Vendor: %provider_name
+Export-Package: 
+ org.eclipse.jgit.http.server;version="0.6.0",
+ org.eclipse.jgit.http.server.glue;version="0.6.0",
+ org.eclipse.jgit.http.server.resolver;version="0.6.0"
+Bundle-ActivationPolicy: lazy
+Bundle-RequiredExecutionEnvironment: J2SE-1.5
+Import-Package: javax.servlet;version="[2.5.0,3.0.0)",
+ javax.servlet.http;version="[2.5.0,3.0.0)",
+ org.eclipse.jgit.errors;version="0.6.0",
+ org.eclipse.jgit.lib;version="0.6.0",
+ org.eclipse.jgit.revwalk;version="0.6.0",
+ org.eclipse.jgit.transport;version="0.6.0",
+ org.eclipse.jgit.util;version="0.6.0",
+ org.eclipse.jgit.util.io;version="0.6.0"
diff --git a/org.eclipse.jgit.http.server/build.properties b/org.eclipse.jgit.http.server/build.properties
new file mode 100644
index 0000000000000000000000000000000000000000..aa1a0082691202e9c8cc650976d2b2d128cae35b
--- /dev/null
+++ b/org.eclipse.jgit.http.server/build.properties
@@ -0,0 +1,5 @@
+source.. = src/
+output.. = bin/
+bin.includes = META-INF/,\
+               .,\
+               plugin.properties
diff --git a/org.eclipse.jgit.http.server/plugin.properties b/org.eclipse.jgit.http.server/plugin.properties
new file mode 100644
index 0000000000000000000000000000000000000000..6ca52145a74ac5d47ac6ec51a57d0871253e3a81
--- /dev/null
+++ b/org.eclipse.jgit.http.server/plugin.properties
@@ -0,0 +1,2 @@
+plugin_name=Java Git HTTP Server (Incubation)
+provider_name=eclipse.org
diff --git a/org.eclipse.jgit.http.server/pom.xml b/org.eclipse.jgit.http.server/pom.xml
new file mode 100644
index 0000000000000000000000000000000000000000..cb49402ae53ef240d532f6a9b0e8e780ff0a51a2
--- /dev/null
+++ b/org.eclipse.jgit.http.server/pom.xml
@@ -0,0 +1,101 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+   Copyright (C) 2009-2010, Google Inc.
+   Copyright (C) 2008, Imran M Yousuf <imyousuf@smartitengineering.com>
+   and other copyright owners as documented in the project's IP log.
+
+   This program and the accompanying materials are made available
+   under the terms of the Eclipse Distribution License v1.0 which
+   accompanies this distribution, is reproduced below, and is
+   available at http://www.eclipse.org/org/documents/edl-v10.php
+
+   All rights reserved.
+
+   Redistribution and use in source and binary forms, with or
+   without modification, are permitted provided that the following
+   conditions are met:
+
+   - Redistributions of source code must retain the above copyright
+     notice, this list of conditions and the following disclaimer.
+
+   - Redistributions in binary form must reproduce the above
+     copyright notice, this list of conditions and the following
+     disclaimer in the documentation and/or other materials provided
+     with the distribution.
+
+   - Neither the name of the Eclipse Foundation, Inc. nor the
+     names of its contributors may be used to endorse or promote
+     products derived from this software without specific prior
+     written permission.
+
+   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+   CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+   INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+   OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+   ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+   CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+   NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+   LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+   CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+   STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+   ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+   ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>org.eclipse.jgit</groupId>
+    <artifactId>org.eclipse.jgit-parent</artifactId>
+    <version>0.6.0-SNAPSHOT</version>
+  </parent>
+
+  <artifactId>org.eclipse.jgit.http.server</artifactId>
+  <name>JGit - HTTP Server</name>
+
+  <description>
+    Git aware HTTP server implementation.
+  </description>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.eclipse.jgit</groupId>
+      <artifactId>org.eclipse.jgit</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+
+    <dependency>
+      <groupId>javax.servlet</groupId>
+      <artifactId>servlet-api</artifactId>
+      <scope>provided</scope>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <sourceDirectory>src/</sourceDirectory>
+
+    <resources>
+      <resource>
+        <directory>.</directory>
+        <includes>
+          <include>plugin.properties</include>
+        </includes>
+      </resource>
+    </resources>
+
+    <plugins>
+      <plugin>
+        <artifactId>maven-jar-plugin</artifactId>
+        <configuration>
+          <archive>
+            <manifestFile>META-INF/MANIFEST.MF</manifestFile>
+          </archive>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+</project>
diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/AsIsFileFilter.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/AsIsFileFilter.java
new file mode 100644
index 0000000000000000000000000000000000000000..810f6942024aa53f5f27a4c1651895001c4e052e
--- /dev/null
+++ b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/AsIsFileFilter.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2009-2010, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ *   copyright notice, this list of conditions and the following
+ *   disclaimer in the documentation and/or other materials provided
+ *   with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ *   names of its contributors may be used to endorse or promote
+ *   products derived from this software without specific prior
+ *   written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.http.server;
+
+import static javax.servlet.http.HttpServletResponse.SC_FORBIDDEN;
+import static javax.servlet.http.HttpServletResponse.SC_UNAUTHORIZED;
+import static org.eclipse.jgit.http.server.ServletUtils.getRepository;
+
+import java.io.IOException;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jgit.http.server.resolver.AsIsFileService;
+import org.eclipse.jgit.http.server.resolver.ServiceNotAuthorizedException;
+import org.eclipse.jgit.http.server.resolver.ServiceNotEnabledException;
+import org.eclipse.jgit.lib.Repository;
+
+class AsIsFileFilter implements Filter {
+	private final AsIsFileService asIs;
+
+	AsIsFileFilter(final AsIsFileService getAnyFile) {
+		this.asIs = getAnyFile;
+	}
+
+	public void init(FilterConfig config) throws ServletException {
+		// Do nothing.
+	}
+
+	public void destroy() {
+		// Do nothing.
+	}
+
+	public void doFilter(ServletRequest request, ServletResponse response,
+			FilterChain chain) throws IOException, ServletException {
+		try {
+			final Repository db = getRepository(request);
+			asIs.access((HttpServletRequest) request, db);
+			chain.doFilter(request, response);
+		} catch (ServiceNotAuthorizedException e) {
+			((HttpServletResponse) response).sendError(SC_UNAUTHORIZED);
+		} catch (ServiceNotEnabledException e) {
+			((HttpServletResponse) response).sendError(SC_FORBIDDEN);
+		}
+	}
+}
diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/FileSender.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/FileSender.java
new file mode 100644
index 0000000000000000000000000000000000000000..280450d01a7cfc8c6fd672100fdaca3404680f61
--- /dev/null
+++ b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/FileSender.java
@@ -0,0 +1,225 @@
+/*
+ * Copyright (C) 2009-2010, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ *   copyright notice, this list of conditions and the following
+ *   disclaimer in the documentation and/or other materials provided
+ *   with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ *   names of its contributors may be used to endorse or promote
+ *   products derived from this software without specific prior
+ *   written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.http.server;
+
+import static javax.servlet.http.HttpServletResponse.SC_PARTIAL_CONTENT;
+import static javax.servlet.http.HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE;
+import static org.eclipse.jgit.util.HttpSupport.HDR_ACCEPT_RANGES;
+import static org.eclipse.jgit.util.HttpSupport.HDR_CONTENT_LENGTH;
+import static org.eclipse.jgit.util.HttpSupport.HDR_CONTENT_RANGE;
+import static org.eclipse.jgit.util.HttpSupport.HDR_IF_RANGE;
+import static org.eclipse.jgit.util.HttpSupport.HDR_RANGE;
+
+import java.io.EOFException;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.RandomAccessFile;
+import java.util.Enumeration;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.util.IO;
+
+/**
+ * Dumps a file over HTTP GET (or its information via HEAD).
+ * <p>
+ * Supports a single byte range requested via {@code Range} HTTP header. This
+ * feature supports a dumb client to resume download of a larger object file.
+ */
+final class FileSender {
+	private final File path;
+
+	private final RandomAccessFile source;
+
+	private final long lastModified;
+
+	private final long fileLen;
+
+	private long pos;
+
+	private long end;
+
+	FileSender(final File path) throws FileNotFoundException {
+		this.path = path;
+		this.source = new RandomAccessFile(path, "r");
+
+		try {
+			this.lastModified = path.lastModified();
+			this.fileLen = source.getChannel().size();
+			this.end = fileLen;
+		} catch (IOException e) {
+			try {
+				source.close();
+			} catch (IOException closeError) {
+				// Ignore any error closing the stream.
+			}
+
+			final FileNotFoundException r;
+			r = new FileNotFoundException("Cannot get length of " + path);
+			r.initCause(e);
+			throw r;
+		}
+	}
+
+	void close() {
+		try {
+			source.close();
+		} catch (IOException e) {
+			// Ignore close errors on a read-only stream.
+		}
+	}
+
+	long getLastModified() {
+		return lastModified;
+	}
+
+	String getTailChecksum() throws IOException {
+		final int n = 20;
+		final byte[] buf = new byte[n];
+		IO.readFully(source.getChannel(), fileLen - n, buf, 0, n);
+		return ObjectId.fromRaw(buf).getName();
+	}
+
+	void serve(final HttpServletRequest req, final HttpServletResponse rsp,
+			final boolean sendBody) throws IOException {
+		if (!initRangeRequest(req, rsp)) {
+			rsp.sendError(SC_REQUESTED_RANGE_NOT_SATISFIABLE);
+			return;
+		}
+
+		rsp.setHeader(HDR_ACCEPT_RANGES, "bytes");
+		rsp.setHeader(HDR_CONTENT_LENGTH, Long.toString(end - pos));
+
+		if (sendBody) {
+			final OutputStream out = rsp.getOutputStream();
+			try {
+				final byte[] buf = new byte[4096];
+				while (pos < end) {
+					final int r = (int) Math.min(buf.length, end - pos);
+					final int n = source.read(buf, 0, r);
+					if (n < 0) {
+						throw new EOFException("Unexpected EOF on " + path);
+					}
+					out.write(buf, 0, n);
+					pos += n;
+				}
+				out.flush();
+			} finally {
+				out.close();
+			}
+		}
+	}
+
+	private boolean initRangeRequest(final HttpServletRequest req,
+			final HttpServletResponse rsp) throws IOException {
+		final Enumeration<String> rangeHeaders = getRange(req);
+		if (!rangeHeaders.hasMoreElements()) {
+			// No range headers, the request is fine.
+			return true;
+		}
+
+		final String range = rangeHeaders.nextElement();
+		if (rangeHeaders.hasMoreElements()) {
+			// To simplify the code we support only one range.
+			return false;
+		}
+
+		final int eq = range.indexOf('=');
+		final int dash = range.indexOf('-');
+		if (eq < 0 || dash < 0 || !range.startsWith("bytes=")) {
+			return false;
+		}
+
+		final String ifRange = req.getHeader(HDR_IF_RANGE);
+		if (ifRange != null && !getTailChecksum().equals(ifRange)) {
+			// If the client asked us to verify the ETag and its not
+			// what they expected we need to send the entire content.
+			return true;
+		}
+
+		try {
+			if (eq + 1 == dash) {
+				// "bytes=-500" means last 500 bytes
+				pos = Long.parseLong(range.substring(dash + 1));
+				pos = fileLen - pos;
+			} else {
+				// "bytes=500-" (position 500 to end)
+				// "bytes=500-1000" (position 500 to 1000)
+				pos = Long.parseLong(range.substring(eq + 1, dash));
+				if (dash < range.length() - 1) {
+					end = Long.parseLong(range.substring(dash + 1));
+					end++; // range was inclusive, want exclusive
+				}
+			}
+		} catch (NumberFormatException e) {
+			// We probably hit here because of a non-digit such as
+			// "," appearing at the end of the first range telling
+			// us there is a second range following. To simplify
+			// the code we support only one range.
+			return false;
+		}
+
+		if (end > fileLen) {
+			end = fileLen;
+		}
+		if (pos >= end) {
+			return false;
+		}
+
+		rsp.setStatus(SC_PARTIAL_CONTENT);
+		rsp.setHeader(HDR_CONTENT_RANGE, "bytes " + pos + "-" + (end - 1) + "/"
+				+ fileLen);
+		source.seek(pos);
+		return true;
+	}
+
+	@SuppressWarnings("unchecked")
+	private static Enumeration<String> getRange(final HttpServletRequest req) {
+		return req.getHeaders(HDR_RANGE);
+	}
+}
diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/GitServlet.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/GitServlet.java
new file mode 100644
index 0000000000000000000000000000000000000000..1ce2776e4dc06ce977b88abb6ec32212ece31531
--- /dev/null
+++ b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/GitServlet.java
@@ -0,0 +1,236 @@
+/*
+ * Copyright (C) 2009-2010, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ *   copyright notice, this list of conditions and the following
+ *   disclaimer in the documentation and/or other materials provided
+ *   with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ *   names of its contributors may be used to endorse or promote
+ *   products derived from this software without specific prior
+ *   written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.http.server;
+
+import java.io.File;
+
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletException;
+
+import org.eclipse.jgit.http.server.glue.MetaServlet;
+import org.eclipse.jgit.http.server.glue.RegexGroupFilter;
+import org.eclipse.jgit.http.server.glue.ServletBinder;
+import org.eclipse.jgit.http.server.resolver.FileResolver;
+import org.eclipse.jgit.http.server.resolver.AsIsFileService;
+import org.eclipse.jgit.http.server.resolver.RepositoryResolver;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.util.StringUtils;
+
+/**
+ * Handles Git repository access over HTTP.
+ * <p>
+ * Applications embedding this servlet should map a directory path within the
+ * application to this servlet, for example:
+ *
+ * <pre>
+ *   &lt;servlet&gt;
+ *     &lt;servlet-name&gt;GitServlet&lt;/servlet-name&gt;
+ *     &lt;servlet-class&gt;org.eclipse.jgit.http.server.GitServlet&lt;/servlet-class&gt;
+ *     &lt;init-param&gt;
+ *       &lt;param-name&gt;base-path&lt;/param-name&gt;
+ *       &lt;param-value&gt;/var/srv/git&lt;/param-value&gt;
+ *     &lt;/init-param&gt;
+ *     &lt;init-param&gt;
+ *       &lt;param-name&gt;export-all&lt;/param-name&gt;
+ *       &lt;param-value&gt;0&lt;/param-value&gt;
+ *     &lt;/init-param&gt;
+ * &lt;/servlet&gt;
+ *   &lt;servlet-mapping&gt;
+ *     &lt;servlet-name&gt;GitServlet&lt;/servlet-name&gt;
+ *     &lt;url-pattern&gt;/git/*&lt;/url-pattern&gt;
+ *   &lt;/servlet-mapping&gt;
+ * </pre>
+ *
+ * <p>
+ * Applications may wish to add additional repository action URLs to this
+ * servlet by taking advantage of its extension from {@link MetaServlet}.
+ * Callers may register their own URL suffix translations through
+ * {@link #serve(String)}, or their regex translations through
+ * {@link #serveRegex(String)}. Each translation should contain a complete
+ * filter pipeline which ends with the HttpServlet that should handle the
+ * requested action.
+ */
+public class GitServlet extends MetaServlet {
+	private static final long serialVersionUID = 1L;
+
+	private volatile boolean initialized;
+
+	private RepositoryResolver resolver;
+
+	private AsIsFileService asIs = new AsIsFileService();
+
+	/**
+	 * New servlet that will load its base directory from {@code web.xml}.
+	 * <p>
+	 * The required parameter {@code base-path} must be configured to point to
+	 * the local filesystem directory where all served Git repositories reside.
+	 */
+	public GitServlet() {
+		// Initialized above by field declarations.
+	}
+
+	/**
+	 * New servlet configured with a specific resolver.
+	 *
+	 * @param resolver
+	 *            the resolver to use when matching URL to Git repository. If
+	 *            null the {@code base-path} parameter will be looked for in the
+	 *            parameter table during init, which usually comes from the
+	 *            {@code web.xml} file of the web application.
+	 */
+	public void setRepositoryResolver(RepositoryResolver resolver) {
+		assertNotInitialized();
+		this.resolver = resolver;
+	}
+
+	/**
+	 * @param f
+	 *            the filter to validate direct access to repository files
+	 *            through a dumb client. If {@code null} then dumb client
+	 *            support is completely disabled.
+	 */
+	public void setAsIsFileService(AsIsFileService f) {
+		assertNotInitialized();
+		this.asIs = f != null ? f : AsIsFileService.DISABLED;
+	}
+
+	private void assertNotInitialized() {
+		if (initialized)
+			throw new IllegalStateException("Already initialized by container");
+	}
+
+	@Override
+	public void init(final ServletConfig config) throws ServletException {
+		super.init(config);
+
+		if (resolver == null) {
+			final File root = getFile("base-path");
+			final boolean exportAll = getBoolean("export-all");
+			setRepositoryResolver(new FileResolver(root, exportAll));
+		}
+
+		initialized = true;
+
+		if (asIs != AsIsFileService.DISABLED) {
+			final IsLocalFilter mustBeLocal = new IsLocalFilter();
+			final AsIsFileFilter enabled = new AsIsFileFilter(asIs);
+
+			serve("*/" + Constants.INFO_REFS)//
+					.through(mustBeLocal)//
+					.through(enabled)//
+					.with(new InfoRefsServlet());
+
+			serve("*/" + Constants.HEAD)//
+					.through(mustBeLocal)//
+					.through(enabled)//
+					.with(new TextFileServlet(Constants.HEAD));
+
+			final String info_alternates = "objects/info/alternates";
+			serve("*/" + info_alternates)//
+					.through(mustBeLocal)//
+					.through(enabled)//
+					.with(new TextFileServlet(info_alternates));
+
+			final String http_alternates = "objects/info/http-alternates";
+			serve("*/" + http_alternates)//
+					.through(mustBeLocal)//
+					.through(enabled)//
+					.with(new TextFileServlet(http_alternates));
+
+			serve("*/objects/info/packs")//
+					.through(mustBeLocal)//
+					.through(enabled)//
+					.with(new InfoPacksServlet());
+
+			serveRegex("^/(.*)/objects/([0-9a-f]{2}/[0-9a-f]{38})$")//
+					.through(mustBeLocal)//
+					.through(enabled)//
+					.through(new RegexGroupFilter(2))//
+					.with(new ObjectFileServlet.Loose());
+
+			serveRegex("^/(.*)/objects/(pack/pack-[0-9a-f]{40}\\.pack)$")//
+					.through(mustBeLocal)//
+					.through(enabled)//
+					.through(new RegexGroupFilter(2))//
+					.with(new ObjectFileServlet.Pack());
+
+			serveRegex("^/(.*)/objects/(pack/pack-[0-9a-f]{40}\\.idx)$")//
+					.through(mustBeLocal)//
+					.through(enabled)//
+					.through(new RegexGroupFilter(2))//
+					.with(new ObjectFileServlet.PackIdx());
+		}
+	}
+
+	private File getFile(final String param) throws ServletException {
+		String n = getInitParameter(param);
+		if (n == null || "".equals(n))
+			throw new ServletException("Parameter " + param + " not set");
+
+		File path = new File(n);
+		if (!path.exists())
+			throw new ServletException(path + " (for " + param + ") not found");
+		return path;
+	}
+
+	private boolean getBoolean(String param) throws ServletException {
+		String n = getInitParameter(param);
+		if (n == null)
+			return false;
+		try {
+			return StringUtils.toBoolean(n);
+		} catch (IllegalArgumentException err) {
+			throw new ServletException("Invalid boolean " + param + " = " + n);
+		}
+	}
+
+	@Override
+	protected ServletBinder register(ServletBinder binder) {
+		if (resolver == null)
+			throw new IllegalStateException("No resolver available");
+		binder = binder.through(new NoCacheFilter());
+		binder = binder.through(new RepositoryFilter(resolver));
+		return binder;
+	}
+}
diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/InfoPacksServlet.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/InfoPacksServlet.java
new file mode 100644
index 0000000000000000000000000000000000000000..dff1e8252e8d9e41b512c64670bb9db3816be807
--- /dev/null
+++ b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/InfoPacksServlet.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2009-2010, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ *   copyright notice, this list of conditions and the following
+ *   disclaimer in the documentation and/or other materials provided
+ *   with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ *   names of its contributors may be used to endorse or promote
+ *   products derived from this software without specific prior
+ *   written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.http.server;
+
+import static org.eclipse.jgit.http.server.ServletUtils.getRepository;
+import static org.eclipse.jgit.http.server.ServletUtils.sendPlainText;
+
+import java.io.IOException;
+
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jgit.lib.ObjectDatabase;
+import org.eclipse.jgit.lib.ObjectDirectory;
+import org.eclipse.jgit.lib.PackFile;
+
+/** Sends the current list of pack files, sorted most recent first. */
+class InfoPacksServlet extends HttpServlet {
+	private static final long serialVersionUID = 1L;
+
+	public void doGet(final HttpServletRequest req,
+			final HttpServletResponse rsp) throws IOException {
+		sendPlainText(packList(req), req, rsp);
+	}
+
+	private static String packList(final HttpServletRequest req) {
+		final StringBuilder out = new StringBuilder();
+		final ObjectDatabase db = getRepository(req).getObjectDatabase();
+		if (db instanceof ObjectDirectory) {
+			for (PackFile pack : ((ObjectDirectory) db).getPacks()) {
+				out.append("P ");
+				out.append(pack.getPackFile().getName());
+				out.append('\n');
+			}
+		}
+		out.append('\n');
+		return out.toString();
+	}
+}
diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/InfoRefsServlet.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/InfoRefsServlet.java
new file mode 100644
index 0000000000000000000000000000000000000000..7bad517170485b53382db90eef16ade9ee5e3548
--- /dev/null
+++ b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/InfoRefsServlet.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2009-2010, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ *   copyright notice, this list of conditions and the following
+ *   disclaimer in the documentation and/or other materials provided
+ *   with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ *   names of its contributors may be used to endorse or promote
+ *   products derived from this software without specific prior
+ *   written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.http.server;
+
+import static org.eclipse.jgit.http.server.ServletUtils.getRepository;
+import static org.eclipse.jgit.http.server.ServletUtils.send;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.revwalk.RevFlag;
+import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.transport.RefAdvertiser;
+import org.eclipse.jgit.util.HttpSupport;
+
+/** Send a complete list of current refs, including peeled values for tags. */
+class InfoRefsServlet extends HttpServlet {
+	private static final long serialVersionUID = 1L;
+
+	public void doGet(final HttpServletRequest req,
+			final HttpServletResponse rsp) throws IOException {
+		// Assume a dumb client and send back the dumb client
+		// version of the info/refs file.
+		final byte[] raw = dumbHttp(req);
+		rsp.setContentType(HttpSupport.TEXT_PLAIN);
+		rsp.setCharacterEncoding(Constants.CHARACTER_ENCODING);
+		send(raw, req, rsp);
+	}
+
+	private byte[] dumbHttp(final HttpServletRequest req) throws IOException {
+		final Repository db = getRepository(req);
+		final RevWalk walk = new RevWalk(db);
+		final RevFlag ADVERTISED = walk.newFlag("ADVERTISED");
+		final StringBuilder out = new StringBuilder();
+		final RefAdvertiser adv = new RefAdvertiser() {
+			@Override
+			protected void writeOne(final CharSequence line) {
+				// Whoever decided that info/refs should use a different
+				// delimiter than the native git:// protocol shouldn't
+				// be allowed to design this sort of stuff. :-(
+				out.append(line.toString().replace(' ', '\t'));
+			}
+
+			@Override
+			protected void end() {
+				// No end marker required for info/refs format.
+			}
+		};
+		adv.init(walk, ADVERTISED);
+		adv.setDerefTags(true);
+
+		Map<String, Ref> refs = new HashMap<String, Ref>(db.getAllRefs());
+		refs.remove(Constants.HEAD);
+		adv.send(refs.values());
+		return out.toString().getBytes(Constants.CHARACTER_ENCODING);
+	}
+}
diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/IsLocalFilter.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/IsLocalFilter.java
new file mode 100644
index 0000000000000000000000000000000000000000..34edf82792c58ff78cb33f5d3898c6af841ccf3f
--- /dev/null
+++ b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/IsLocalFilter.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2009-2010, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ *   copyright notice, this list of conditions and the following
+ *   disclaimer in the documentation and/or other materials provided
+ *   with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ *   names of its contributors may be used to endorse or promote
+ *   products derived from this software without specific prior
+ *   written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.http.server;
+
+import static javax.servlet.http.HttpServletResponse.SC_FORBIDDEN;
+import static org.eclipse.jgit.http.server.ServletUtils.getRepository;
+
+import java.io.IOException;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jgit.lib.ObjectDirectory;
+import org.eclipse.jgit.lib.Repository;
+
+/**
+ * Requires the target {@link Repository} to be available via local filesystem.
+ * <p>
+ * The target {@link Repository} must be using a {@link ObjectDirectory}, so the
+ * downstream servlet can directly access its contents on disk.
+ */
+class IsLocalFilter implements Filter {
+	public void init(FilterConfig config) throws ServletException {
+		// Do nothing.
+	}
+
+	public void destroy() {
+		// Do nothing.
+	}
+
+	public void doFilter(ServletRequest request, ServletResponse response,
+			FilterChain chain) throws IOException, ServletException {
+		if (isLocal(getRepository(request)))
+			chain.doFilter(request, response);
+		else
+			((HttpServletResponse) response).sendError(SC_FORBIDDEN);
+	}
+
+	private static boolean isLocal(final Repository db) {
+		return db.getObjectDatabase() instanceof ObjectDirectory;
+	}
+}
diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/NoCacheFilter.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/NoCacheFilter.java
new file mode 100644
index 0000000000000000000000000000000000000000..6a23cb95f5071bfdc2bb1892fef9102b95789cf4
--- /dev/null
+++ b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/NoCacheFilter.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2010, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ *   copyright notice, this list of conditions and the following
+ *   disclaimer in the documentation and/or other materials provided
+ *   with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ *   names of its contributors may be used to endorse or promote
+ *   products derived from this software without specific prior
+ *   written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.http.server;
+
+import static org.eclipse.jgit.util.HttpSupport.HDR_CACHE_CONTROL;
+import static org.eclipse.jgit.util.HttpSupport.HDR_EXPIRES;
+import static org.eclipse.jgit.util.HttpSupport.HDR_PRAGMA;
+
+import java.io.IOException;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletResponse;
+
+/** Adds HTTP response headers to prevent caching by proxies/browsers. */
+class NoCacheFilter implements Filter {
+	public void init(FilterConfig config) throws ServletException {
+		// Do nothing.
+	}
+
+	public void destroy() {
+		// Do nothing.
+	}
+
+	public void doFilter(ServletRequest request, ServletResponse response,
+			FilterChain chain) throws IOException, ServletException {
+		HttpServletResponse rsp = (HttpServletResponse) response;
+
+		rsp.setHeader(HDR_EXPIRES, "Fri, 01 Jan 1980 00:00:00 GMT");
+		rsp.setHeader(HDR_PRAGMA, "no-cache");
+
+		final String nocache = "no-cache, max-age=0, must-revalidate";
+		rsp.setHeader(HDR_CACHE_CONTROL, nocache);
+
+		chain.doFilter(request, response);
+	}
+}
diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/ObjectFileServlet.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/ObjectFileServlet.java
new file mode 100644
index 0000000000000000000000000000000000000000..5d774a82484e8e2c2c7cf46096700419cc524cc9
--- /dev/null
+++ b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/ObjectFileServlet.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2009-2010, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ *   copyright notice, this list of conditions and the following
+ *   disclaimer in the documentation and/or other materials provided
+ *   with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ *   names of its contributors may be used to endorse or promote
+ *   products derived from this software without specific prior
+ *   written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.http.server;
+
+import static javax.servlet.http.HttpServletResponse.SC_NOT_FOUND;
+import static javax.servlet.http.HttpServletResponse.SC_NOT_MODIFIED;
+import static org.eclipse.jgit.http.server.ServletUtils.getRepository;
+import static org.eclipse.jgit.util.HttpSupport.HDR_ETAG;
+import static org.eclipse.jgit.util.HttpSupport.HDR_IF_MODIFIED_SINCE;
+import static org.eclipse.jgit.util.HttpSupport.HDR_IF_NONE_MATCH;
+import static org.eclipse.jgit.util.HttpSupport.HDR_LAST_MODIFIED;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jgit.lib.ObjectDirectory;
+import org.eclipse.jgit.lib.Repository;
+
+/** Sends any object from {@code GIT_DIR/objects/??/0 38}, or any pack file. */
+abstract class ObjectFileServlet extends HttpServlet {
+	private static final long serialVersionUID = 1L;
+
+	static class Loose extends ObjectFileServlet {
+		private static final long serialVersionUID = 1L;
+
+		Loose() {
+			super("application/x-git-loose-object");
+		}
+
+		@Override
+		String etag(final FileSender sender) throws IOException {
+			return Long.toHexString(sender.getLastModified());
+		}
+	}
+
+	private static abstract class PackData extends ObjectFileServlet {
+		private static final long serialVersionUID = 1L;
+
+		PackData(String contentType) {
+			super(contentType);
+		}
+
+		@Override
+		String etag(final FileSender sender) throws IOException {
+			return sender.getTailChecksum();
+		}
+	}
+
+	static class Pack extends PackData {
+		private static final long serialVersionUID = 1L;
+
+		Pack() {
+			super("application/x-git-packed-objects");
+		}
+	}
+
+	static class PackIdx extends PackData {
+		private static final long serialVersionUID = 1L;
+
+		PackIdx() {
+			super("application/x-git-packed-objects-toc");
+		}
+	}
+
+	private final String contentType;
+
+	ObjectFileServlet(final String contentType) {
+		this.contentType = contentType;
+	}
+
+	abstract String etag(FileSender sender) throws IOException;
+
+	@Override
+	public void doGet(final HttpServletRequest req,
+			final HttpServletResponse rsp) throws IOException {
+		serve(req, rsp, true);
+	}
+
+	@Override
+	protected void doHead(final HttpServletRequest req,
+			final HttpServletResponse rsp) throws ServletException, IOException {
+		serve(req, rsp, false);
+	}
+
+	private void serve(final HttpServletRequest req,
+			final HttpServletResponse rsp, final boolean sendBody)
+			throws IOException {
+		final File obj = new File(objects(req), req.getPathInfo());
+		final FileSender sender;
+		try {
+			sender = new FileSender(obj);
+		} catch (FileNotFoundException e) {
+			rsp.sendError(SC_NOT_FOUND);
+			return;
+		}
+
+		try {
+			final String etag = etag(sender);
+			final long lastModified = (sender.getLastModified() / 1000) * 1000;
+
+			String ifNoneMatch = req.getHeader(HDR_IF_NONE_MATCH);
+			if (etag != null && etag.equals(ifNoneMatch)) {
+				rsp.sendError(SC_NOT_MODIFIED);
+				return;
+			}
+
+			long ifModifiedSince = req.getDateHeader(HDR_IF_MODIFIED_SINCE);
+			if (0 < lastModified && lastModified < ifModifiedSince) {
+				rsp.sendError(SC_NOT_MODIFIED);
+				return;
+			}
+
+			if (etag != null)
+				rsp.setHeader(HDR_ETAG, etag);
+			if (0 < lastModified)
+				rsp.setDateHeader(HDR_LAST_MODIFIED, lastModified);
+			rsp.setContentType(contentType);
+			sender.serve(req, rsp, sendBody);
+		} finally {
+			sender.close();
+		}
+	}
+
+	private static File objects(final HttpServletRequest req) {
+		final Repository db = getRepository(req);
+		return ((ObjectDirectory) db.getObjectDatabase()).getDirectory();
+	}
+}
diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/RepositoryFilter.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/RepositoryFilter.java
new file mode 100644
index 0000000000000000000000000000000000000000..a212f0d7ba728bd271d20e8f9919f2bd04fe17e6
--- /dev/null
+++ b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/RepositoryFilter.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2009-2010, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ *   copyright notice, this list of conditions and the following
+ *   disclaimer in the documentation and/or other materials provided
+ *   with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ *   names of its contributors may be used to endorse or promote
+ *   products derived from this software without specific prior
+ *   written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.http.server;
+
+import static javax.servlet.http.HttpServletResponse.SC_FORBIDDEN;
+import static javax.servlet.http.HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
+import static javax.servlet.http.HttpServletResponse.SC_NOT_FOUND;
+import static javax.servlet.http.HttpServletResponse.SC_UNAUTHORIZED;
+import static org.eclipse.jgit.http.server.ServletUtils.ATTRIBUTE_REPOSITORY;
+
+import java.io.IOException;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jgit.errors.RepositoryNotFoundException;
+import org.eclipse.jgit.http.server.resolver.RepositoryResolver;
+import org.eclipse.jgit.http.server.resolver.ServiceNotAuthorizedException;
+import org.eclipse.jgit.http.server.resolver.ServiceNotEnabledException;
+import org.eclipse.jgit.lib.Repository;
+
+/**
+ * Opens a repository named by the path info through {@link RepositoryResolver}.
+ * <p>
+ * This filter assumes it is invoked by {@link GitServlet} and is likely to not
+ * work as expected if called from any other class. This filter assumes the path
+ * info of the current request is a repository name which can be used by the
+ * configured {@link RepositoryResolver} to open a {@link Repository} and attach
+ * it to the current request.
+ * <p>
+ * This filter sets request attribute {@link ServletUtils#ATTRIBUTE_REPOSITORY}
+ * when it discovers the repository, and automatically closes and removes the
+ * attribute when the request is complete.
+ */
+public class RepositoryFilter implements Filter {
+	private final RepositoryResolver resolver;
+
+	private ServletContext context;
+
+	/**
+	 * Create a new filter.
+	 *
+	 * @param resolver
+	 *            the resolver which will be used to translate the URL name
+	 *            component to the actual {@link Repository} instance for the
+	 *            current web request.
+	 */
+	public RepositoryFilter(final RepositoryResolver resolver) {
+		this.resolver = resolver;
+	}
+
+	public void init(final FilterConfig config) throws ServletException {
+		context = config.getServletContext();
+	}
+
+	public void destroy() {
+		context = null;
+	}
+
+	public void doFilter(final ServletRequest request,
+			final ServletResponse rsp, final FilterChain chain)
+			throws IOException, ServletException {
+		if (request.getAttribute(ATTRIBUTE_REPOSITORY) != null) {
+			context.log("Internal server error, request attribute "
+					+ ATTRIBUTE_REPOSITORY + " was already set when "
+					+ getClass().getName() + " was invoked.");
+			((HttpServletResponse) rsp).sendError(SC_INTERNAL_SERVER_ERROR);
+			return;
+		}
+
+		final HttpServletRequest req = (HttpServletRequest) request;
+
+		String name = req.getPathInfo();
+		if (name == null || name.length() == 0) {
+			((HttpServletResponse) rsp).sendError(SC_NOT_FOUND);
+			return;
+		}
+		if (name.startsWith("/"))
+			name = name.substring(1);
+
+		final Repository db;
+		try {
+			db = resolver.open(req, name);
+		} catch (RepositoryNotFoundException e) {
+			((HttpServletResponse) rsp).sendError(SC_NOT_FOUND);
+			return;
+		} catch (ServiceNotAuthorizedException e) {
+			((HttpServletResponse) rsp).sendError(SC_UNAUTHORIZED);
+			return;
+		} catch (ServiceNotEnabledException e) {
+			((HttpServletResponse) rsp).sendError(SC_FORBIDDEN);
+			return;
+		}
+		try {
+			request.setAttribute(ATTRIBUTE_REPOSITORY, db);
+			chain.doFilter(request, rsp);
+		} finally {
+			request.removeAttribute(ATTRIBUTE_REPOSITORY);
+			db.close();
+		}
+	}
+}
diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/ServletUtils.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/ServletUtils.java
new file mode 100644
index 0000000000000000000000000000000000000000..d6b039246f47c190931d0738b394d935dbba943c
--- /dev/null
+++ b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/ServletUtils.java
@@ -0,0 +1,216 @@
+/*
+ * Copyright (C) 2009-2010, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ *   copyright notice, this list of conditions and the following
+ *   disclaimer in the documentation and/or other materials provided
+ *   with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ *   names of its contributors may be used to endorse or promote
+ *   products derived from this software without specific prior
+ *   written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.http.server;
+
+import static org.eclipse.jgit.util.HttpSupport.ENCODING_GZIP;
+import static org.eclipse.jgit.util.HttpSupport.HDR_ACCEPT_ENCODING;
+import static org.eclipse.jgit.util.HttpSupport.HDR_CONTENT_ENCODING;
+import static org.eclipse.jgit.util.HttpSupport.HDR_ETAG;
+import static org.eclipse.jgit.util.HttpSupport.TEXT_PLAIN;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.security.MessageDigest;
+import java.util.zip.GZIPInputStream;
+import java.util.zip.GZIPOutputStream;
+
+import javax.servlet.ServletRequest;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.Repository;
+
+/** Common utility functions for servlets. */
+public final class ServletUtils {
+	/** Request attribute which stores the {@link Repository} instance. */
+	public static final String ATTRIBUTE_REPOSITORY = "org.eclipse.jgit.Repository";
+
+	/**
+	 * Get the selected repository from the request.
+	 *
+	 * @param req
+	 *            the current request.
+	 * @return the repository; never null.
+	 * @throws IllegalStateException
+	 *             the repository was not set by the filter, the servlet is
+	 *             being invoked incorrectly and the programmer should ensure
+	 *             the filter runs before the servlet.
+	 * @see #ATTRIBUTE_REPOSITORY
+	 */
+	public static Repository getRepository(final ServletRequest req) {
+		Repository db = (Repository) req.getAttribute(ATTRIBUTE_REPOSITORY);
+		if (db == null)
+			throw new IllegalStateException("Expected Repository attribute");
+		return db;
+	}
+
+	/**
+	 * Open the request input stream, automatically inflating if necessary.
+	 * <p>
+	 * This method automatically inflates the input stream if the request
+	 * {@code Content-Encoding} header was set to {@code gzip} or the legacy
+	 * {@code x-gzip}.
+	 *
+	 * @param req
+	 *            the incoming request whose input stream needs to be opened.
+	 * @return an input stream to read the raw, uncompressed request body.
+	 * @throws IOException
+	 *             if an input or output exception occurred.
+	 */
+	public static InputStream getInputStream(final HttpServletRequest req)
+			throws IOException {
+		InputStream in = req.getInputStream();
+		final String enc = req.getHeader(HDR_CONTENT_ENCODING);
+		if (ENCODING_GZIP.equals(enc) || "x-gzip".equals(enc)) //$NON-NLS-1$
+			in = new GZIPInputStream(in);
+		else if (enc != null)
+			throw new IOException(HDR_CONTENT_ENCODING + " \"" + enc + "\""
+					+ ": not supported by this library.");
+		return in;
+	}
+
+	/**
+	 * Send a plain text response to a {@code GET} or {@code HEAD} HTTP request.
+	 * <p>
+	 * The text response is encoded in the Git character encoding, UTF-8.
+	 * <p>
+	 * If the user agent supports a compressed transfer encoding and the content
+	 * is large enough, the content may be compressed before sending.
+	 * <p>
+	 * The {@code ETag} and {@code Content-Length} headers are automatically set
+	 * by this method. {@code Content-Encoding} is conditionally set if the user
+	 * agent supports a compressed transfer. Callers are responsible for setting
+	 * any cache control headers.
+	 *
+	 * @param content
+	 *            to return to the user agent as this entity's body.
+	 * @param req
+	 *            the incoming request.
+	 * @param rsp
+	 *            the outgoing response.
+	 * @throws IOException
+	 *             the servlet API rejected sending the body.
+	 */
+	public static void sendPlainText(final String content,
+			final HttpServletRequest req, final HttpServletResponse rsp)
+			throws IOException {
+		final byte[] raw = content.getBytes(Constants.CHARACTER_ENCODING);
+		rsp.setContentType(TEXT_PLAIN);
+		rsp.setCharacterEncoding(Constants.CHARACTER_ENCODING);
+		send(raw, req, rsp);
+	}
+
+	/**
+	 * Send a response to a {@code GET} or {@code HEAD} HTTP request.
+	 * <p>
+	 * If the user agent supports a compressed transfer encoding and the content
+	 * is large enough, the content may be compressed before sending.
+	 * <p>
+	 * The {@code ETag} and {@code Content-Length} headers are automatically set
+	 * by this method. {@code Content-Encoding} is conditionally set if the user
+	 * agent supports a compressed transfer. Callers are responsible for setting
+	 * {@code Content-Type} and any cache control headers.
+	 *
+	 * @param content
+	 *            to return to the user agent as this entity's body.
+	 * @param req
+	 *            the incoming request.
+	 * @param rsp
+	 *            the outgoing response.
+	 * @throws IOException
+	 *             the servlet API rejected sending the body.
+	 */
+	public static void send(byte[] content, final HttpServletRequest req,
+			final HttpServletResponse rsp) throws IOException {
+		content = sendInit(content, req, rsp);
+		final OutputStream out = rsp.getOutputStream();
+		try {
+			out.write(content);
+			out.flush();
+		} finally {
+			out.close();
+		}
+	}
+
+	private static byte[] sendInit(byte[] content,
+			final HttpServletRequest req, final HttpServletResponse rsp)
+			throws IOException {
+		rsp.setHeader(HDR_ETAG, etag(content));
+		if (256 < content.length && acceptsGzipEncoding(req)) {
+			content = compress(content);
+			rsp.setHeader(HDR_CONTENT_ENCODING, ENCODING_GZIP);
+		}
+		rsp.setContentLength(content.length);
+		return content;
+	}
+
+	private static boolean acceptsGzipEncoding(final HttpServletRequest req) {
+		final String accepts = req.getHeader(HDR_ACCEPT_ENCODING);
+		return accepts != null && 0 <= accepts.indexOf(ENCODING_GZIP);
+	}
+
+	private static byte[] compress(final byte[] raw) throws IOException {
+		final int maxLen = raw.length + 32;
+		final ByteArrayOutputStream out = new ByteArrayOutputStream(maxLen);
+		final GZIPOutputStream gz = new GZIPOutputStream(out);
+		gz.write(raw);
+		gz.finish();
+		gz.flush();
+		return out.toByteArray();
+	}
+
+	private static String etag(final byte[] content) {
+		final MessageDigest md = Constants.newMessageDigest();
+		md.update(content);
+		return ObjectId.fromRaw(md.digest()).getName();
+	}
+
+	private ServletUtils() {
+		// static utility class only
+	}
+}
diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/TextFileServlet.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/TextFileServlet.java
new file mode 100644
index 0000000000000000000000000000000000000000..5bf5546cf7ce8ff5cc83a236cbec7b968cefcd79
--- /dev/null
+++ b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/TextFileServlet.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2009-2010, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ *   copyright notice, this list of conditions and the following
+ *   disclaimer in the documentation and/or other materials provided
+ *   with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ *   names of its contributors may be used to endorse or promote
+ *   products derived from this software without specific prior
+ *   written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.http.server;
+
+import static javax.servlet.http.HttpServletResponse.SC_NOT_FOUND;
+import static org.eclipse.jgit.http.server.ServletUtils.getRepository;
+import static org.eclipse.jgit.http.server.ServletUtils.send;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jgit.util.HttpSupport;
+import org.eclipse.jgit.util.IO;
+
+/** Sends a small text meta file from the repository. */
+class TextFileServlet extends HttpServlet {
+	private static final long serialVersionUID = 1L;
+
+	private final String fileName;
+
+	TextFileServlet(final String name) {
+		this.fileName = name;
+	}
+
+	public void doGet(final HttpServletRequest req,
+			final HttpServletResponse rsp) throws IOException {
+		try {
+			rsp.setContentType(HttpSupport.TEXT_PLAIN);
+			send(read(req), req, rsp);
+		} catch (FileNotFoundException noFile) {
+			rsp.sendError(SC_NOT_FOUND);
+		}
+	}
+
+	private byte[] read(final HttpServletRequest req) throws IOException {
+		final File gitdir = getRepository(req).getDirectory();
+		return IO.readFully(new File(gitdir, fileName));
+	}
+}
diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/glue/ErrorServlet.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/glue/ErrorServlet.java
new file mode 100644
index 0000000000000000000000000000000000000000..c0a9e0e6084e8554e1677fd0809c021c7bb602ba
--- /dev/null
+++ b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/glue/ErrorServlet.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2010, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ *   copyright notice, this list of conditions and the following
+ *   disclaimer in the documentation and/or other materials provided
+ *   with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ *   names of its contributors may be used to endorse or promote
+ *   products derived from this software without specific prior
+ *   written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.http.server.glue;
+
+import java.io.IOException;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/** Sends a fixed status code to the client. */
+public class ErrorServlet extends HttpServlet {
+	private static final long serialVersionUID = 1L;
+
+	private final int status;
+
+	/**
+	 * Sends a specific status code.
+	 *
+	 * @param status
+	 *            the HTTP status code to always send.
+	 */
+	public ErrorServlet(final int status) {
+		this.status = status;
+	}
+
+	@Override
+	protected void doGet(HttpServletRequest req, HttpServletResponse rsp)
+			throws ServletException, IOException {
+		rsp.sendError(status);
+	}
+}
diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/glue/MetaServlet.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/glue/MetaServlet.java
new file mode 100644
index 0000000000000000000000000000000000000000..d289743ba497fb54dce9a182a871383b2001e0c9
--- /dev/null
+++ b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/glue/MetaServlet.java
@@ -0,0 +1,210 @@
+/*
+ * Copyright (C) 2009-2010, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ *   copyright notice, this list of conditions and the following
+ *   disclaimer in the documentation and/or other materials provided
+ *   with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ *   names of its contributors may be used to endorse or promote
+ *   products derived from this software without specific prior
+ *   written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.http.server.glue;
+
+import static javax.servlet.http.HttpServletResponse.SC_NOT_FOUND;
+
+import java.io.IOException;
+import java.util.AbstractSet;
+import java.util.ArrayList;
+import java.util.IdentityHashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * Generic container servlet to manage routing to different pipelines.
+ * <p>
+ * Callers can create and configure a new processing pipeline by using one of
+ * the {@link #serve(String)} or {@link #serveRegex(String)} methods to allocate
+ * a binder for a particular URL pattern.
+ * <p>
+ * Registered filters and servlets are initialized lazily, usually during the
+ * first request. Once initialized the bindings in this servlet cannot be
+ * modified without destroying the servlet and thereby destroying all registered
+ * filters and servlets.
+ */
+public class MetaServlet extends HttpServlet {
+	private static final long serialVersionUID = 1L;
+
+	static final String REGEX_GROUPS = "org.eclipse.jgit.http.server.glue.MetaServlet.serveRegex";
+
+	private final List<ServletBinderImpl> bindings;
+
+	private volatile UrlPipeline[] pipelines;
+
+	/** Empty servlet with no bindings. */
+	public MetaServlet() {
+		this.bindings = new ArrayList<ServletBinderImpl>();
+	}
+
+	/**
+	 * Construct a binding for a specific path.
+	 *
+	 * @param path
+	 *            pattern to match.
+	 * @return binder for the passed path.
+	 */
+	public ServletBinder serve(String path) {
+		if (path.startsWith("*"))
+			return register(new SuffixPipeline.Binder(path.substring(1)));
+		throw new IllegalArgumentException("\"" + path + "\" not supported");
+	}
+
+	/**
+	 * Construct a binding for a regular expression.
+	 *
+	 * @param expression
+	 *            the regular expression to pattern match the URL against.
+	 * @return binder for the passed expression.
+	 */
+	public ServletBinder serveRegex(String expression) {
+		return register(new RegexPipeline.Binder(expression));
+	}
+
+	public void destroy() {
+		if (pipelines != null) {
+			Set<Object> destroyed = newIdentitySet();
+			for (UrlPipeline p : pipelines)
+				p.destroy(destroyed);
+			pipelines = null;
+		}
+	}
+
+	private static Set<Object> newIdentitySet() {
+		final Map<Object, Object> m = new IdentityHashMap<Object, Object>();
+		return new AbstractSet<Object>() {
+			@Override
+			public boolean add(Object o) {
+				return m.put(o, o) == null;
+			}
+
+			@Override
+			public boolean contains(Object o) {
+				return m.keySet().contains(o);
+			}
+
+			@Override
+			public Iterator<Object> iterator() {
+				return m.keySet().iterator();
+			}
+
+			@Override
+			public int size() {
+				return m.size();
+			}
+		};
+	}
+
+	@Override
+	protected void service(final HttpServletRequest req,
+			final HttpServletResponse rsp) throws ServletException, IOException {
+		final UrlPipeline p = find(req);
+		if (p != null)
+			p.service(req, rsp);
+		else
+			rsp.sendError(SC_NOT_FOUND);
+	}
+
+	private UrlPipeline find(final HttpServletRequest req)
+			throws ServletException {
+		for (UrlPipeline p : getPipelines())
+			if (p.match(req))
+				return p;
+		return null;
+	}
+
+	private ServletBinder register(ServletBinderImpl b) {
+		synchronized (bindings) {
+			if (pipelines != null)
+				throw new IllegalStateException("Servlet already initialized");
+			bindings.add(b);
+		}
+		return register((ServletBinder) b);
+	}
+
+	/**
+	 * Configure a newly created binder.
+	 *
+	 * @param b
+	 *            the newly created binder.
+	 * @return binder for the caller, potentially after adding one or more
+	 *         filters into the pipeline.
+	 */
+	protected ServletBinder register(ServletBinder b) {
+		return b;
+	}
+
+	private UrlPipeline[] getPipelines() throws ServletException {
+		UrlPipeline[] r = pipelines;
+		if (r == null) {
+			synchronized (bindings) {
+				r = pipelines;
+				if (r == null) {
+					r = createPipelines();
+					pipelines = r;
+				}
+			}
+		}
+		return r;
+	}
+
+	private UrlPipeline[] createPipelines() throws ServletException {
+		UrlPipeline[] array = new UrlPipeline[bindings.size()];
+
+		for (int i = 0; i < bindings.size(); i++)
+			array[i] = bindings.get(i).create();
+
+		Set<Object> inited = newIdentitySet();
+		for (UrlPipeline p : array)
+			p.init(getServletContext(), inited);
+		return array;
+	}
+}
diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/glue/RegexGroupFilter.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/glue/RegexGroupFilter.java
new file mode 100644
index 0000000000000000000000000000000000000000..ed7b1cf6942c30a3792603c0036811313b32d01f
--- /dev/null
+++ b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/glue/RegexGroupFilter.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2009-2010, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ *   copyright notice, this list of conditions and the following
+ *   disclaimer in the documentation and/or other materials provided
+ *   with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ *   names of its contributors may be used to endorse or promote
+ *   products derived from this software without specific prior
+ *   written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.http.server.glue;
+
+import java.io.IOException;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+
+/**
+ * Switch servlet path and path info to use another regex match group.
+ * <p>
+ * This filter is meant to be installed in the middle of a pipeline created by
+ * {@link MetaServlet#serveRegex(String)}. The passed request's servlet path is
+ * updated to be all text up to the start of the designated capture group, and
+ * the path info is changed to the contents of the capture group.
+ **/
+public class RegexGroupFilter implements Filter {
+	private final int groupIdx;
+
+	/**
+	 * @param groupIdx
+	 *            capture group number, 1 through the number of groups.
+	 */
+	public RegexGroupFilter(final int groupIdx) {
+		if (groupIdx < 1)
+			throw new IllegalArgumentException("Invalid index: " + groupIdx);
+		this.groupIdx = groupIdx - 1;
+	}
+
+	public void init(FilterConfig config) throws ServletException {
+		// Do nothing.
+	}
+
+	public void destroy() {
+		// Do nothing.
+	}
+
+	public void doFilter(final ServletRequest request,
+			final ServletResponse rsp, final FilterChain chain)
+			throws IOException, ServletException {
+		final WrappedRequest[] g = groupsFor(request);
+		if (groupIdx < g.length)
+			chain.doFilter(g[groupIdx], rsp);
+		else
+			throw new ServletException("Invalid regex group " + (groupIdx + 1));
+	}
+
+	private static WrappedRequest[] groupsFor(final ServletRequest r) {
+		return (WrappedRequest[]) r.getAttribute(MetaServlet.REGEX_GROUPS);
+	}
+}
diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/glue/RegexPipeline.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/glue/RegexPipeline.java
new file mode 100644
index 0000000000000000000000000000000000000000..635ff5493fd433d0819862dcaf5b7a5b73806028
--- /dev/null
+++ b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/glue/RegexPipeline.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2009-2010, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ *   copyright notice, this list of conditions and the following
+ *   disclaimer in the documentation and/or other materials provided
+ *   with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ *   names of its contributors may be used to endorse or promote
+ *   products derived from this software without specific prior
+ *   written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.http.server.glue;
+
+import static javax.servlet.http.HttpServletResponse.SC_NOT_FOUND;
+import static org.eclipse.jgit.http.server.glue.MetaServlet.REGEX_GROUPS;
+
+import java.io.IOException;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javax.servlet.Filter;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * Selects requests by matching the URI against a regular expression.
+ * <p>
+ * The pattern is bound and matched against the path info of the servlet
+ * request, as this class assumes it is invoked by {@link MetaServlet}.
+ * <p>
+ * If there are capture groups in the regular expression, the matched ranges of
+ * the capture groups are stored as an array of modified HttpServetRequests,
+ * into the request attribute {@link MetaServlet#REGEX_GROUPS}.
+ * <p>
+ * Each servlet request has been altered to have its {@code getPathInfo()}
+ * method return the matched text of the corresponding capture group. A
+ * {@link RegexGroupFilter} can be applied in the pipeline to switch the current
+ * HttpServletRequest to reference a different capture group before running
+ * additional filters, or the final servlet.
+ * <p>
+ * This class dispatches the remainder of the pipeline using the first capture
+ * group as the current request, making {@code RegexGroupFilter} required only
+ * to access capture groups beyond the first.
+ */
+class RegexPipeline extends UrlPipeline {
+	static class Binder extends ServletBinderImpl {
+		private final Pattern pattern;
+
+		Binder(final String p) {
+			pattern = Pattern.compile(p);
+		}
+
+		UrlPipeline create() {
+			return new RegexPipeline(pattern, getFilters(), getServlet());
+		}
+	}
+
+	private final Pattern pattern;
+
+	RegexPipeline(final Pattern pattern, final Filter[] filters,
+			final HttpServlet servlet) {
+		super(filters, servlet);
+		this.pattern = pattern;
+	}
+
+	boolean match(final HttpServletRequest req) {
+		final String pathInfo = req.getPathInfo();
+		return pathInfo != null && pattern.matcher(pathInfo).matches();
+	}
+
+	@Override
+	void service(HttpServletRequest req, HttpServletResponse rsp)
+			throws ServletException, IOException {
+		final String reqInfo = req.getPathInfo();
+		if (reqInfo == null) {
+			rsp.sendError(SC_NOT_FOUND);
+			return;
+		}
+
+		final Matcher cur = pattern.matcher(reqInfo);
+		if (!cur.matches()) {
+			rsp.sendError(SC_NOT_FOUND);
+			return;
+		}
+
+		final String reqPath = req.getServletPath();
+		final Object old = req.getAttribute(REGEX_GROUPS);
+		try {
+			if (1 <= cur.groupCount()) {
+				// If there are groups extract every capture group and
+				// build a request for them so RegexGroupFilter can pick
+				// a different capture group later. Continue using the
+				// first capture group as the path info.
+				WrappedRequest groups[] = new WrappedRequest[cur.groupCount()];
+				for (int groupId = 1; groupId <= cur.groupCount(); groupId++) {
+					final int s = cur.start(groupId);
+					final String path, info;
+
+					path = reqPath + reqInfo.substring(0, s);
+					info = cur.group(groupId);
+					groups[groupId - 1] = new WrappedRequest(req, path, info);
+				}
+				req.setAttribute(REGEX_GROUPS, groups);
+				super.service(groups[0], rsp);
+
+			} else {
+				// No capture groups were present, service the whole request.
+				final String path = reqPath + reqInfo;
+				final String info = null;
+				super.service(new WrappedRequest(req, path, info), rsp);
+			}
+		} finally {
+			if (old != null)
+				req.setAttribute(REGEX_GROUPS, old);
+			else
+				req.removeAttribute(REGEX_GROUPS);
+		}
+	}
+
+	@Override
+	public String toString() {
+		return "Pipeline[regex: " + pattern + " ]";
+	}
+}
diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/glue/ServletBinder.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/glue/ServletBinder.java
new file mode 100644
index 0000000000000000000000000000000000000000..9c3ed50d089b850556acfd39f77c33effbfdf263
--- /dev/null
+++ b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/glue/ServletBinder.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2009-2010, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ *   copyright notice, this list of conditions and the following
+ *   disclaimer in the documentation and/or other materials provided
+ *   with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ *   names of its contributors may be used to endorse or promote
+ *   products derived from this software without specific prior
+ *   written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.http.server.glue;
+
+import javax.servlet.Filter;
+import javax.servlet.http.HttpServlet;
+
+/** Binds a servlet to a URL. */
+public interface ServletBinder {
+	/**
+	 * @param filter
+	 *            the filter to trigger while processing the path.
+	 * @return {@code this}.
+	 */
+	public ServletBinder through(Filter filter);
+
+	/**
+	 * @param servlet
+	 *            the servlet to execute on this path.
+	 */
+	public void with(HttpServlet servlet);
+}
\ No newline at end of file
diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/glue/ServletBinderImpl.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/glue/ServletBinderImpl.java
new file mode 100644
index 0000000000000000000000000000000000000000..d4cd445a1324585c895dded8a160c6cda765edd0
--- /dev/null
+++ b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/glue/ServletBinderImpl.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2009-2010, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ *   copyright notice, this list of conditions and the following
+ *   disclaimer in the documentation and/or other materials provided
+ *   with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ *   names of its contributors may be used to endorse or promote
+ *   products derived from this software without specific prior
+ *   written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.http.server.glue;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.servlet.Filter;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletResponse;
+
+abstract class ServletBinderImpl implements ServletBinder {
+	private final List<Filter> filters;
+
+	private HttpServlet httpServlet;
+
+	ServletBinderImpl() {
+		this.filters = new ArrayList<Filter>();
+	}
+
+	public ServletBinder through(Filter filter) {
+		if (filter == null)
+			throw new NullPointerException("filter must not be null");
+		filters.add(filter);
+		return this;
+	}
+
+	public void with(HttpServlet servlet) {
+		if (servlet == null)
+			throw new NullPointerException("servlet must not be null");
+		if (httpServlet != null)
+			throw new IllegalStateException("servlet was already bound");
+		httpServlet = servlet;
+	}
+
+	/** @return the configured servlet, or singleton returning 404 if none. */
+	protected HttpServlet getServlet() {
+		if (httpServlet != null)
+			return httpServlet;
+		else
+			return new ErrorServlet(HttpServletResponse.SC_NOT_FOUND);
+	}
+
+	/** @return the configured filters; zero-length array if none. */
+	protected Filter[] getFilters() {
+		return filters.toArray(new Filter[filters.size()]);
+	}
+
+	/** @return the pipeline that matches and executes this chain. */
+	abstract UrlPipeline create();
+}
diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/glue/SuffixPipeline.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/glue/SuffixPipeline.java
new file mode 100644
index 0000000000000000000000000000000000000000..b9420162591b07f50e0cea7ca156f1eecb0ecb18
--- /dev/null
+++ b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/glue/SuffixPipeline.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2009-2010, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ *   copyright notice, this list of conditions and the following
+ *   disclaimer in the documentation and/or other materials provided
+ *   with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ *   names of its contributors may be used to endorse or promote
+ *   products derived from this software without specific prior
+ *   written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.http.server.glue;
+
+import java.io.IOException;
+
+import javax.servlet.Filter;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * Selects requests by matching the suffix of the URI.
+ * <p>
+ * The suffix string is literally matched against the path info of the servlet
+ * request, as this class assumes it is invoked by {@link MetaServlet}. Suffix
+ * strings may include path components. Examples include {@code /info/refs}, or
+ * just simple extension matches like {@code .txt}.
+ * <p>
+ * When dispatching to the rest of the pipeline the HttpServletRequest is
+ * modified so that {@code getPathInfo()} does not contain the suffix that
+ * caused this pipeline to be selected.
+ */
+class SuffixPipeline extends UrlPipeline {
+	static class Binder extends ServletBinderImpl {
+		private final String suffix;
+
+		Binder(final String suffix) {
+			this.suffix = suffix;
+		}
+
+		UrlPipeline create() {
+			return new SuffixPipeline(suffix, getFilters(), getServlet());
+		}
+	}
+
+	private final String suffix;
+
+	private final int suffixLen;
+
+	SuffixPipeline(final String suffix, final Filter[] filters,
+			final HttpServlet servlet) {
+		super(filters, servlet);
+		this.suffix = suffix;
+		this.suffixLen = suffix.length();
+	}
+
+	boolean match(final HttpServletRequest req) {
+		final String pathInfo = req.getPathInfo();
+		return pathInfo != null && pathInfo.endsWith(suffix);
+	}
+
+	@Override
+	void service(HttpServletRequest req, HttpServletResponse rsp)
+			throws ServletException, IOException {
+		String curInfo = req.getPathInfo();
+		String newPath = req.getServletPath() + curInfo;
+		String newInfo = curInfo.substring(0, curInfo.length() - suffixLen);
+		super.service(new WrappedRequest(req, newPath, newInfo), rsp);
+	}
+
+	@Override
+	public String toString() {
+		return "Pipeline[ *" + suffix + " ]";
+	}
+}
diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/glue/UrlPipeline.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/glue/UrlPipeline.java
new file mode 100644
index 0000000000000000000000000000000000000000..2257966d69e4b0293c5fd8d2a457f9618787beec
--- /dev/null
+++ b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/glue/UrlPipeline.java
@@ -0,0 +1,264 @@
+/*
+ * Copyright (C) 2009-2010, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ *   copyright notice, this list of conditions and the following
+ *   disclaimer in the documentation and/or other materials provided
+ *   with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ *   names of its contributors may be used to endorse or promote
+ *   products derived from this software without specific prior
+ *   written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.http.server.glue;
+
+import java.io.IOException;
+import java.util.Enumeration;
+import java.util.NoSuchElementException;
+import java.util.Set;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * Encapsulates the entire serving stack for a single URL.
+ * <p>
+ * Subclasses provide the implementation of {@link #match(HttpServletRequest)},
+ * which is called by {@link MetaServlet} in registration order to determine the
+ * pipeline that will be used to handle a request.
+ * <p>
+ * The very bottom of each pipeline is a single {@link HttpServlet} that will
+ * handle producing the response for this pipeline's URL. {@link Filter}s may
+ * also be registered and applied around the servlet's processing, to manage
+ * request attributes, set standard response headers, or completely override the
+ * response generation.
+ */
+abstract class UrlPipeline {
+	/** Filters to apply around {@link #servlet}; may be empty but never null. */
+	private final Filter[] filters;
+
+	/** Instance that must generate the response; never null. */
+	private final HttpServlet servlet;
+
+	UrlPipeline(final Filter[] filters, final HttpServlet servlet) {
+		this.filters = filters;
+		this.servlet = servlet;
+	}
+
+	/**
+	 * Initialize all contained filters and servlets.
+	 *
+	 * @param context
+	 *            the servlet container context our {@link MetaServlet} is
+	 *            running within.
+	 * @param inited
+	 *            <i>(input/output)</i> the set of filters and servlets which
+	 *            have already been initialized within the container context. If
+	 *            those same instances appear in this pipeline they are not
+	 *            initialized a second time. Filters and servlets that are first
+	 *            initialized by this pipeline will be added to this set.
+	 * @throws ServletException
+	 *             a filter or servlet is unable to initialize.
+	 */
+	void init(final ServletContext context, final Set<Object> inited)
+			throws ServletException {
+		for (Filter ref : filters)
+			initFilter(ref, context, inited);
+		initServlet(servlet, context, inited);
+	}
+
+	private static void initFilter(final Filter ref,
+			final ServletContext context, final Set<Object> inited)
+			throws ServletException {
+		if (!inited.contains(ref)) {
+			ref.init(new FilterConfig() {
+				public String getInitParameter(String name) {
+					return null;
+				}
+
+				public Enumeration getInitParameterNames() {
+					return new Enumeration<String>() {
+						public boolean hasMoreElements() {
+							return false;
+						}
+
+						public String nextElement() {
+							throw new NoSuchElementException();
+						}
+					};
+				}
+
+				public ServletContext getServletContext() {
+					return context;
+				}
+
+				public String getFilterName() {
+					return ref.getClass().getName();
+				}
+			});
+			inited.add(ref);
+		}
+	}
+
+	private static void initServlet(final HttpServlet ref,
+			final ServletContext context, final Set<Object> inited)
+			throws ServletException {
+		if (!inited.contains(ref)) {
+			ref.init(new ServletConfig() {
+				public String getInitParameter(String name) {
+					return null;
+				}
+
+				public Enumeration getInitParameterNames() {
+					return new Enumeration<String>() {
+						public boolean hasMoreElements() {
+							return false;
+						}
+
+						public String nextElement() {
+							throw new NoSuchElementException();
+						}
+					};
+				}
+
+				public ServletContext getServletContext() {
+					return context;
+				}
+
+				public String getServletName() {
+					return ref.getClass().getName();
+				}
+			});
+			inited.add(ref);
+		}
+	}
+
+	/**
+	 * Destroy all contained filters and servlets.
+	 *
+	 * @param destroyed
+	 *            <i>(input/output)</i> the set of filters and servlets which
+	 *            have already been destroyed within the container context. If
+	 *            those same instances appear in this pipeline they are not
+	 *            destroyed a second time. Filters and servlets that are first
+	 *            destroyed by this pipeline will be added to this set.
+	 */
+	void destroy(final Set<Object> destroyed) {
+		for (Filter ref : filters)
+			destroyFilter(ref, destroyed);
+		destroyServlet(servlet, destroyed);
+	}
+
+	private static void destroyFilter(Filter ref, Set<Object> destroyed) {
+		if (!destroyed.contains(ref)) {
+			ref.destroy();
+			destroyed.add(ref);
+		}
+	}
+
+	private static void destroyServlet(HttpServlet ref, Set<Object> destroyed) {
+		if (!destroyed.contains(ref)) {
+			ref.destroy();
+			destroyed.add(ref);
+		}
+	}
+
+	/**
+	 * Determine if this pipeline handles the request's URL.
+	 * <p>
+	 * This method should match on the request's {@code getPathInfo()} method,
+	 * as {@link MetaServlet} passes the request along as-is to each pipeline's
+	 * match method.
+	 *
+	 * @param req
+	 *            current HTTP request being considered by {@link MetaServlet}.
+	 * @return {@code true} if this pipeline is configured to handle the
+	 *         request; {@code false} otherwise.
+	 */
+	abstract boolean match(HttpServletRequest req);
+
+	/**
+	 * Execute the filters and the servlet on the request.
+	 * <p>
+	 * Invoked by {@link MetaServlet} once {@link #match(HttpServletRequest)}
+	 * has determined this pipeline is the correct pipeline to handle the
+	 * current request.
+	 *
+	 * @param req
+	 *            current HTTP request.
+	 * @param rsp
+	 *            current HTTP response.
+	 * @throws ServletException
+	 *             request cannot be completed.
+	 * @throws IOException
+	 *             IO error prevents the request from being completed.
+	 */
+	void service(HttpServletRequest req, HttpServletResponse rsp)
+			throws ServletException, IOException {
+		if (0 < filters.length)
+			new Chain(filters, servlet).doFilter(req, rsp);
+		else
+			servlet.service(req, rsp);
+	}
+
+	private static class Chain implements FilterChain {
+		private final Filter[] filters;
+
+		private final HttpServlet servlet;
+
+		private int filterIdx;
+
+		Chain(final Filter[] filters, final HttpServlet servlet) {
+			this.filters = filters;
+			this.servlet = servlet;
+		}
+
+		public void doFilter(ServletRequest req, ServletResponse rsp)
+				throws IOException, ServletException {
+			if (filterIdx < filters.length)
+				filters[filterIdx++].doFilter(req, rsp, this);
+			else
+				servlet.service(req, rsp);
+		}
+	}
+}
diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/glue/WrappedRequest.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/glue/WrappedRequest.java
new file mode 100644
index 0000000000000000000000000000000000000000..7f8da7c440fc45bc80d13588a16fec4d2b30c04b
--- /dev/null
+++ b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/glue/WrappedRequest.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2009-2010, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ *   copyright notice, this list of conditions and the following
+ *   disclaimer in the documentation and/or other materials provided
+ *   with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ *   names of its contributors may be used to endorse or promote
+ *   products derived from this software without specific prior
+ *   written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.http.server.glue;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletRequestWrapper;
+
+/** Overrides the path and path info. */
+public class WrappedRequest extends HttpServletRequestWrapper {
+	private final String path;
+
+	private final String pathInfo;
+
+	/**
+	 * Create a new request with different path and path info properties.
+	 *
+	 * @param originalRequest
+	 *            the original HTTP request.
+	 * @param path
+	 *            new servlet path to report to callers.
+	 * @param pathInfo
+	 *            new path info to report to callers.
+	 */
+	public WrappedRequest(final HttpServletRequest originalRequest,
+			final String path, final String pathInfo) {
+		super(originalRequest);
+		this.path = path;
+		this.pathInfo = pathInfo;
+	}
+
+	@Override
+	public String getPathTranslated() {
+		final String p = getPathInfo();
+		return p != null ? getRealPath(p) : null;
+	}
+
+	@Override
+	public String getPathInfo() {
+		return pathInfo;
+	}
+
+	@Override
+	public String getServletPath() {
+		return path;
+	}
+}
diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/resolver/AsIsFileService.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/resolver/AsIsFileService.java
new file mode 100644
index 0000000000000000000000000000000000000000..b9545f9101fde3d5af899d605e3d222ff7d62eb4
--- /dev/null
+++ b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/resolver/AsIsFileService.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2009-2010, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ *   copyright notice, this list of conditions and the following
+ *   disclaimer in the documentation and/or other materials provided
+ *   with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ *   names of its contributors may be used to endorse or promote
+ *   products derived from this software without specific prior
+ *   written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.http.server.resolver;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.eclipse.jgit.http.server.GitServlet;
+import org.eclipse.jgit.lib.Config;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.lib.Config.SectionParser;
+
+/**
+ * Controls access to bare files in a repository.
+ * <p>
+ * Older HTTP clients which do not speak the smart HTTP variant of the Git
+ * protocol fetch from a repository by directly getting its objects and pack
+ * files. This class, along with the {@code http.getanyfile} per-repository
+ * configuration setting, can be used by {@link GitServlet} to control whether
+ * or not these older clients are permitted to read these direct files.
+ */
+public class AsIsFileService {
+	/** Always throws {@link ServiceNotEnabledException}. */
+	public static final AsIsFileService DISABLED = new AsIsFileService() {
+		@Override
+		public void access(HttpServletRequest req, Repository db)
+				throws ServiceNotEnabledException {
+			throw new ServiceNotEnabledException();
+		}
+	};
+
+	private static final SectionParser<ServiceConfig> CONFIG = new SectionParser<ServiceConfig>() {
+		public ServiceConfig parse(final Config cfg) {
+			return new ServiceConfig(cfg);
+		}
+	};
+
+	private static class ServiceConfig {
+		final boolean enabled;
+
+		ServiceConfig(final Config cfg) {
+			enabled = cfg.getBoolean("http", "getanyfile", true);
+		}
+	}
+
+	/**
+	 * Determine if {@code http.getanyfile} is enabled in the configuration.
+	 *
+	 * @param db
+	 *            the repository to check.
+	 * @return {@code false} if {@code http.getanyfile} was explicitly set to
+	 *         {@code false} in the repository's configuration file; otherwise
+	 *         {@code true}.
+	 */
+	protected static boolean isEnabled(Repository db) {
+		return db.getConfig().get(CONFIG).enabled;
+	}
+
+	/**
+	 * Determine if access to any bare file of the repository is allowed.
+	 * <p>
+	 * This method silently succeeds if the request is allowed, or fails by
+	 * throwing a checked exception if access should be denied.
+	 * <p>
+	 * The default implementation of this method checks {@code http.getanyfile},
+	 * throwing {@link ServiceNotEnabledException} if it was explicitly set to
+	 * {@code false}, and otherwise succeeding silently.
+	 *
+	 * @param req
+	 *            current HTTP request, in case information from the request may
+	 *            help determine the access request.
+	 * @param db
+	 *            the repository the request would obtain a bare file from.
+	 * @throws ServiceNotEnabledException
+	 *             bare file access is not allowed on the target repository, by
+	 *             any user, for any reason.
+	 * @throws ServiceNotAuthorizedException
+	 *             bare file access is not allowed for this HTTP request and
+	 *             repository, such as due to a permission error.
+	 */
+	public void access(HttpServletRequest req, Repository db)
+			throws ServiceNotEnabledException, ServiceNotAuthorizedException {
+		if (!isEnabled(db))
+			throw new ServiceNotEnabledException();
+	}
+}
diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/resolver/FileResolver.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/resolver/FileResolver.java
new file mode 100644
index 0000000000000000000000000000000000000000..82a0ce84a04faffc6027c90266d3e05fc62cd35b
--- /dev/null
+++ b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/resolver/FileResolver.java
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2009-2010, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ *   copyright notice, this list of conditions and the following
+ *   disclaimer in the documentation and/or other materials provided
+ *   with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ *   names of its contributors may be used to endorse or promote
+ *   products derived from this software without specific prior
+ *   written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.http.server.resolver;
+
+import java.io.File;
+import java.io.IOException;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.eclipse.jgit.errors.RepositoryNotFoundException;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.lib.RepositoryCache;
+import org.eclipse.jgit.lib.RepositoryCache.FileKey;
+
+/** Default resolver serving from a single root path in local filesystem. */
+public class FileResolver implements RepositoryResolver {
+	private final File basePath;
+
+	private final boolean exportAll;
+
+	/**
+	 * Create a new resolver for the given path.
+	 *
+	 * @param basePath
+	 *            the base path all repositories are rooted under.
+	 * @param exportAll
+	 *            if true, exports all repositories, ignoring the check for the
+	 *            {@code git-daemon-export-ok} files.
+	 */
+	public FileResolver(final File basePath, final boolean exportAll) {
+		this.basePath = basePath;
+		this.exportAll = exportAll;
+	}
+
+	public Repository open(final HttpServletRequest req,
+			final String repositoryName) throws RepositoryNotFoundException,
+			ServiceNotEnabledException {
+		if (isUnreasonableName(repositoryName))
+			throw new RepositoryNotFoundException(repositoryName);
+
+		final Repository db;
+		try {
+			final File gitdir = new File(basePath, repositoryName);
+			db = RepositoryCache.open(FileKey.lenient(gitdir), true);
+		} catch (IOException e) {
+			throw new RepositoryNotFoundException(repositoryName, e);
+		}
+
+		try {
+			if (isExportOk(req, repositoryName, db)) {
+				// We have to leak the open count to the caller, they
+				// are responsible for closing the repository if we
+				// complete successfully.
+				return db;
+			} else
+				throw new ServiceNotEnabledException();
+
+		} catch (RuntimeException e) {
+			db.close();
+			throw new RepositoryNotFoundException(repositoryName, e);
+
+		} catch (IOException e) {
+			db.close();
+			throw new RepositoryNotFoundException(repositoryName, e);
+
+		} catch (ServiceNotEnabledException e) {
+			db.close();
+			throw e;
+		}
+	}
+
+	/** @return {@code true} if all repositories are to be exported. */
+	protected boolean isExportAll() {
+		return exportAll;
+	}
+
+	/**
+	 * Check if this repository can be served over HTTP.
+	 * <p>
+	 * The default implementation of this method returns true only if either
+	 * {@link #isExportAll()} is true, or the {@code git-daemon-export-ok} file
+	 * is present in the repository's directory.
+	 *
+	 * @param req
+	 *            the current HTTP request.
+	 * @param repositoryName
+	 *            name of the repository, as present in the URL.
+	 * @param db
+	 *            the opened repository instance.
+	 * @return true if the repository is accessible; false if not.
+	 * @throws IOException
+	 *             the repository could not be accessed, the caller will claim
+	 *             the repository does not exist.
+	 */
+	protected boolean isExportOk(HttpServletRequest req, String repositoryName,
+			Repository db) throws IOException {
+		if (isExportAll())
+			return true;
+		else
+			return new File(db.getDirectory(), "git-daemon-export-ok").exists();
+	}
+
+	private static boolean isUnreasonableName(final String name) {
+		if (name.length() == 0)
+			return true; // no empty paths
+
+		if (name.indexOf('\\') >= 0)
+			return true; // no windows/dos style paths
+		if (new File(name).isAbsolute())
+			return true; // no absolute paths
+
+		if (name.startsWith("../"))
+			return true; // no "l../etc/passwd"
+		if (name.contains("/../"))
+			return true; // no "foo/../etc/passwd"
+		if (name.contains("/./"))
+			return true; // "foo/./foo" is insane to ask
+		if (name.contains("//"))
+			return true; // double slashes is sloppy, don't use it
+
+		return false; // is a reasonable name
+	}
+}
diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/resolver/RepositoryResolver.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/resolver/RepositoryResolver.java
new file mode 100644
index 0000000000000000000000000000000000000000..ba17dac45bcc231a1aec9b52b73a8afa75610603
--- /dev/null
+++ b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/resolver/RepositoryResolver.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2009-2010, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ *   copyright notice, this list of conditions and the following
+ *   disclaimer in the documentation and/or other materials provided
+ *   with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ *   names of its contributors may be used to endorse or promote
+ *   products derived from this software without specific prior
+ *   written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.http.server.resolver;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.eclipse.jgit.errors.RepositoryNotFoundException;
+import org.eclipse.jgit.lib.Repository;
+
+/** Locate a Git {@link Repository} by name from the URL. */
+public interface RepositoryResolver {
+	/**
+	 * Locate and open a reference to a {@link Repository}.
+	 * <p>
+	 * The caller is responsible for closing the returned Repository.
+	 *
+	 * @param req
+	 *            the current HTTP request, may be used to inspect session state
+	 *            including cookies or user authentication.
+	 * @param name
+	 *            name of the repository, as parsed out of the URL.
+	 * @return the opened repository instance, never null.
+	 * @throws RepositoryNotFoundException
+	 *             the repository does not exist or the name is incorrectly
+	 *             formatted as a repository name.
+	 * @throws ServiceNotAuthorizedException
+	 *             the repository exists, but HTTP access is not allowed for the
+	 *             current user.
+	 * @throws ServiceNotEnabledException
+	 *             the repository exists, but HTTP access is not allowed on the
+	 *             target repository, by any user.
+	 */
+	Repository open(HttpServletRequest req, String name)
+			throws RepositoryNotFoundException, ServiceNotAuthorizedException,
+			ServiceNotEnabledException;
+}
diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/resolver/ServiceNotAuthorizedException.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/resolver/ServiceNotAuthorizedException.java
new file mode 100644
index 0000000000000000000000000000000000000000..fca044a10cce527f3cc75e7ccc6fc4463e3b2820
--- /dev/null
+++ b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/resolver/ServiceNotAuthorizedException.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2009-2010, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ *   copyright notice, this list of conditions and the following
+ *   disclaimer in the documentation and/or other materials provided
+ *   with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ *   names of its contributors may be used to endorse or promote
+ *   products derived from this software without specific prior
+ *   written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.http.server.resolver;
+
+/** Indicates the request service is not authorized for current user. */
+public class ServiceNotAuthorizedException extends Exception {
+	private static final long serialVersionUID = 1L;
+
+	/** Indicates the request service is not available. */
+	public ServiceNotAuthorizedException() {
+		super("Service not permitted");
+	}
+}
diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/resolver/ServiceNotEnabledException.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/resolver/ServiceNotEnabledException.java
new file mode 100644
index 0000000000000000000000000000000000000000..dedc0d48251173b2aafbb2a0ba0bf2ce0328594f
--- /dev/null
+++ b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/resolver/ServiceNotEnabledException.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2009-2010, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ *   copyright notice, this list of conditions and the following
+ *   disclaimer in the documentation and/or other materials provided
+ *   with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ *   names of its contributors may be used to endorse or promote
+ *   products derived from this software without specific prior
+ *   written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.http.server.resolver;
+
+/** Indicates the request service is not enabled on a repository. */
+public class ServiceNotEnabledException extends Exception {
+	private static final long serialVersionUID = 1L;
+
+	/** Indicates the request service is not available. */
+	public ServiceNotEnabledException() {
+		super("Service not enabled");
+	}
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/errors/RepositoryNotFoundException.java b/org.eclipse.jgit/src/org/eclipse/jgit/errors/RepositoryNotFoundException.java
index d947a2cdc7c7aee13a556e4ad6c34d3d271656cb..c745e7326e0a266f1ceea5c67d419c3b56ec3780 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/errors/RepositoryNotFoundException.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/errors/RepositoryNotFoundException.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009, Google Inc.
+ * Copyright (C) 2009-2010, Google Inc.
  * and other copyright owners as documented in the project's IP log.
  *
  * This program and the accompanying materials are made available
@@ -59,6 +59,18 @@ public RepositoryNotFoundException(final File location) {
 		this(location.getPath());
 	}
 
+	/**
+	 * Constructs an exception indicating a local repository does not exist.
+	 *
+	 * @param location
+	 *            description of the repository not found, usually file path.
+	 * @param why
+	 *            why the repository does not exist.
+	 */
+	public RepositoryNotFoundException(final File location, Throwable why) {
+		this(location.getPath(), why);
+	}
+
 	/**
 	 * Constructs an exception indicating a local repository does not exist.
 	 *
@@ -66,6 +78,22 @@ public RepositoryNotFoundException(final File location) {
 	 *            description of the repository not found, usually file path.
 	 */
 	public RepositoryNotFoundException(final String location) {
-		super("repository not found: " + location);
+		super(message(location));
+	}
+
+	/**
+	 * Constructs an exception indicating a local repository does not exist.
+	 *
+	 * @param location
+	 *            description of the repository not found, usually file path.
+	 * @param why
+	 *            why the repository does not exist.
+	 */
+	public RepositoryNotFoundException(String location, Throwable why) {
+		super(message(location), why);
+	}
+
+	private static String message(final String location) {
+		return "repository not found: " + location;
 	}
 }
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Config.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Config.java
index b6b9c5e05bc071db586769d30cdeb66ef3674b83..661371ca21c5b4f31e468b361b195e8d7eee2387 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Config.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Config.java
@@ -1,7 +1,7 @@
 /*
  * Copyright (C) 2009, Constantine Plotnikov <constantine.plotnikov@gmail.com>
  * Copyright (C) 2007, Dave Watson <dwatson@mimvista.com>
- * Copyright (C) 2008-2009, Google Inc.
+ * Copyright (C) 2008-2010, Google Inc.
  * Copyright (C) 2009, Google, Inc.
  * Copyright (C) 2009, JetBrains s.r.o.
  * Copyright (C) 2007-2008, Robin Rosenberg <robin.rosenberg@dewire.com>
@@ -298,20 +298,11 @@ public boolean getBoolean(final String section, String subsection,
 		String n = getRawString(section, subsection, name);
 		if (n == null)
 			return defaultValue;
-
-		if (MAGIC_EMPTY_VALUE == n || StringUtils.equalsIgnoreCase("yes", n)
-				|| StringUtils.equalsIgnoreCase("true", n)
-				|| StringUtils.equalsIgnoreCase("1", n)
-				|| StringUtils.equalsIgnoreCase("on", n)) {
+		if (MAGIC_EMPTY_VALUE == n)
 			return true;
-
-		} else if (StringUtils.equalsIgnoreCase("no", n)
-				|| StringUtils.equalsIgnoreCase("false", n)
-				|| StringUtils.equalsIgnoreCase("0", n)
-				|| StringUtils.equalsIgnoreCase("off", n)) {
-			return false;
-
-		} else {
+		try {
+			return StringUtils.toBoolean(n);
+		} catch (IllegalArgumentException err) {
 			throw new IllegalArgumentException("Invalid boolean value: "
 					+ section + "." + name + "=" + n);
 		}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/HttpSupport.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/HttpSupport.java
index 3910c8bd7ffbb8a31f2efd39f2b55ea764a79d56..8ce2ff06ec8cd61cc06d028b19eb47cbac4a215a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/HttpSupport.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/HttpSupport.java
@@ -1,4 +1,5 @@
 /*
+ * Copyright (C) 2010, Google Inc.
  * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
  * and other copyright owners as documented in the project's IP log.
  *
@@ -55,6 +56,63 @@
 
 /** Extra utilities to support usage of HTTP. */
 public class HttpSupport {
+	/** The {@code POST} HTTP method. */
+	public static final String METHOD_POST = "POST";
+
+	/** The {@code Cache-Control} header. */
+	public static final String HDR_CACHE_CONTROL = "Cache-Control";
+
+	/** The {@code Pragma} header. */
+	public static final String HDR_PRAGMA = "Pragma";
+
+	/** The {@code Date} header. */
+	public static final String HDR_DATE = "Date";
+
+	/** The {@code Expires} header. */
+	public static final String HDR_EXPIRES = "Expires";
+
+	/** The {@code ETag} header. */
+	public static final String HDR_ETAG = "ETag";
+
+	/** The {@code If-None-Match} header. */
+	public static final String HDR_IF_NONE_MATCH = "If-None-Match";
+
+	/** The {@code Last-Modified} header. */
+	public static final String HDR_LAST_MODIFIED = "Last-Modified";
+
+	/** The {@code If-Modified-Since} header. */
+	public static final String HDR_IF_MODIFIED_SINCE = "If-Modified-Since";
+
+	/** The {@code Content-Type} header. */
+	public static final String HDR_CONTENT_TYPE = "Content-Type";
+
+	/** The {@code Content-Length} header. */
+	public static final String HDR_CONTENT_LENGTH = "Content-Length";
+
+	/** The {@code Content-Encoding} header. */
+	public static final String HDR_CONTENT_ENCODING = "Content-Encoding";
+
+	/** The {@code Content-Range} header. */
+	public static final String HDR_CONTENT_RANGE = "Content-Range";
+
+	/** The {@code Accept-Ranges} header. */
+	public static final String HDR_ACCEPT_RANGES = "Accept-Ranges";
+
+	/** The {@code If-Range} header. */
+	public static final String HDR_IF_RANGE = "If-Range";
+
+	/** The {@code Range} header. */
+	public static final String HDR_RANGE = "Range";
+
+	/** The {@code Accept-Encoding} header. */
+	public static final String HDR_ACCEPT_ENCODING = "Accept-Encoding";
+
+	/** The {@code gzip} encoding value for {@link #HDR_ACCEPT_ENCODING}. */
+	public static final String ENCODING_GZIP = "gzip";
+
+	/** The standard {@code text/plain} MIME type. */
+	public static final String TEXT_PLAIN = "text/plain";
+
 	/**
 	 * URL encode a value string into an output buffer.
 	 *
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/StringUtils.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/StringUtils.java
index ddde48d6cf62fcc2c8962d709974f445c1f67de2..582dce8afff8b657a5016a7b0ce050920e74d9f2 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/StringUtils.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/StringUtils.java
@@ -115,6 +115,45 @@ public static boolean equalsIgnoreCase(final String a, final String b) {
 		return true;
 	}
 
+	/**
+	 * Parse a string as a standard Git boolean value.
+	 * <p>
+	 * The terms {@code yes}, {@code true}, {@code 1}, {@code on} can all be
+	 * used to mean {@code true}.
+	 * <p>
+	 * The terms {@code no}, {@code false}, {@code 0}, {@code off} can all be
+	 * used to mean {@code false}.
+	 * <p>
+	 * Comparisons ignore case, via {@link #equalsIgnoreCase(String, String)}.
+	 *
+	 * @param stringValue
+	 *            the string to parse.
+	 * @return the boolean interpretation of {@code value}.
+	 * @throws IllegalArgumentException
+	 *             if {@code value} is not recognized as one of the standard
+	 *             boolean names.
+	 */
+	public static boolean toBoolean(final String stringValue) {
+		if (stringValue == null)
+			throw new NullPointerException("Expected boolean string value");
+
+		if (equalsIgnoreCase("yes", stringValue)
+				|| equalsIgnoreCase("true", stringValue)
+				|| equalsIgnoreCase("1", stringValue)
+				|| equalsIgnoreCase("on", stringValue)) {
+			return true;
+
+		} else if (equalsIgnoreCase("no", stringValue)
+				|| equalsIgnoreCase("false", stringValue)
+				|| equalsIgnoreCase("0", stringValue)
+				|| equalsIgnoreCase("off", stringValue)) {
+			return false;
+
+		} else {
+			throw new IllegalArgumentException("Not a boolean: " + stringValue);
+		}
+	}
+
 	private StringUtils() {
 		// Do not create instances
 	}
diff --git a/pom.xml b/pom.xml
index a5887288c9de109703303c5da259b4dee108fc44..d3467204f4c794ae5fbe95a8d3524d45bdd9bf1d 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <!--
-   Copyright (C) 2009, Google Inc.
+   Copyright (C) 2009-2010, Google Inc.
    and other copyright owners as documented in the project's IP log.
 
    This program and the accompanying materials are made available
@@ -135,6 +135,9 @@
 
     <args4j-CQ>CQ 3454</args4j-CQ>
     <args4j-version>2.0.12</args4j-version>
+
+    <servlet-api-CQ>CQ 3565</servlet-api-CQ>
+    <servlet-api-version>2.5</servlet-api-version>
   </properties>
 
   <build>
@@ -256,6 +259,12 @@
         <artifactId>junit</artifactId>
         <version>${junit-version}</version>
       </dependency>
+
+      <dependency>
+        <groupId>javax.servlet</groupId>
+        <artifactId>servlet-api</artifactId>
+        <version>${servlet-api-version}</version>
+      </dependency>
     </dependencies>
   </dependencyManagement>
 
@@ -286,6 +295,7 @@
   <modules>
     <module>org.eclipse.jgit</module>
     <module>org.eclipse.jgit.ui</module>
+    <module>org.eclipse.jgit.http.server</module>
     <module>org.eclipse.jgit.pgm</module>
     <module>org.eclipse.jgit.junit</module>
     <module>org.eclipse.jgit.test</module>